I want to model the following simple relationship:
One Passenger belongs to a Car; One Car has many Passengers.
The passenger table has an id and Car_id column, the Car table has one id column.
My models look like this:
<?php
class Passenger extends AppModel {
var $name = 'Passenger';
var $belongsTo = 'Car';
} ?>
and
<?php
class Car extends AppModel {
var $name = 'Car';
var $hasMany = array (
'Passenger' => array (
'className' => 'Passenger',
'foreignKey' => 'car_id'
)
);
}
?>
and my add Passenger .ctp looks like this:
<?php
echo $this->Form->create('Passenger');
echo $this->Form->input('car_id');
echo $this->Form->end('Save');
?>
BUt when I access the page to add a passenger, all I see is an empty drop down box. Is there an additional step I must take in order to populate the dropbox with all cars?
First off, you have forgotten to mention the belongsTo relation in your Passenger model:
<?php
class Passenger extends AppModel {
var $name = 'Passenger';
var $belongsTo = array('Car');
}
?>
Next, in the corresponding action of your controller, you will need to obtain a list of all the cars from the database, and set it to the plural form of the model's variable ($cars). You would do that like so:
$cars = $this->Passenger->Car->find('list');
$this->set(compact('cars'));
This will convert the car_id input field into a drop down list with the populated information.
HTH.
The Passenger will only know about the car with which it is associated - at this point, none.
In the add method in the passenger controller, do
$this->Car->find('list');
and pass the result into your view:
$this->set('cars',$cars);
In the view, give the $cars variable as the value for $options in the field declaration:
echo $this->Form->input('car_id', array('options' => $cars));
Alternatively, you can do something like:
echo $this->Form->input('Car.id', array('options' => $cars));
$this->CompanyCashback->bindModel(array('belongsTo' => array(
'CompanyBranch' => array('className' => 'CompanyBranch', 'foreignKey' => false, 'conditions' => array('CompanyCashback.publisher_id = CompanyBranch.publisher_id && CompanyBranch.branch_type = "online" ')),
'PersonalInformation' => array('className' => 'PersonalInformation', 'foreignKey' => false, 'conditions' => array('CompanyCashback.publisher_id = PersonalInformation.user_id')),
'Country' => array('className' => 'Country', 'foreignKey' => false, 'conditions' => array('PersonalInformation.country_id = Country.id')),
'User' => array('className' => 'User', 'foreignKey' => false, 'conditions' => array('PersonalInformation.user_id = User.id')))
));
Related
My 'Reservation' model and 'Profile' model have hasAndBelongsToMany association.
Here is my Reservation Model.
class Reservation extends AppModel {
.
.
var $hasAndBelongsToMany = array(
'Profile' => array(
'className' => 'Profile',
'joinTable' => 'profiles_reservations',
'foreignKey' => 'reservation_id',
'associationForeignKey' => 'profile_id',
'unique' => true,
)
);
And Here is my Profile Model.
class Profile extends AppModel {
var $name = 'Profile';
}
And here is my controller .
function prac3($lname, $fname) {
$profiles = $this->Profile->find('all', array(
'conditions' => array(
'Profile.lname LIKE' => $lname.'%',
'Profile.fname LIKE' => '%'.$fname.'%'
),
'order'=>array( 'Profile.created DESC' ),
));
$this->set('profiles', $profiles);
}
And here is my view.
<?php
if($profiles) {
foreach($profiles as $key => $profile): ?>
<tr>
<td><?= $profile['Profile']['id'] ?></td>
<td><?= $profile['Profile']['lname'] ?></td>
<td><?= $profile['Profile']['fname'] ?></td>
<td><?= $profile['Profile']['home_phone'] ?></td>
</tr>
endforeach;
echo '</table>';
}
?>
I wanna get ['Reservation']['name'] in the view using Profile model. How can I do this?
Update Your Profile class
class Profile extends AppModel {
var $name = 'Profile';
var $hasAndBelongsToMany = array(
'Reservation' => array(
'className' => 'Reservation',
'joinTable' => 'profiles_reservations',
'foreignKey' => 'profile_id',
'associationForeignKey' => 'reservation_id',
'unique' => true, // More about update below
)
}
In Your find() method shoud use recursive:
$profiles = $this->Profile->find('all', array(
//...
'recursive' => 2,
));
or Containable behavior:
$this->Profile->Behaviors->load('Containable');
$profiles = $this->Profile->find('all', array(
//...
'contain' => array(
'Reservation',
),
'recursive' => -1,
));
and Your reservetion will be in array like $profiles['Reservation'][n]['name'].
Additional, in Your comment You wrote "When I add new reservation and profile, the rows that has the profile id were disappeared in the profiles_reservations Table."
Because You are using 'unique' => true in $hasAndBelongsToMany property. The cookbook says:
If true (default value) CakePHP will first delete existing relationship records in the foreign keys table before inserting new ones. Existing associations need to be passed again when updating.
See: https://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasandbelongstomany-habtm
Try this
class ReservationsTable extends Table
{
public function initialize(array $config)
{
$this->belongsToMany('Profiles', [
'joinTable' => 'reservation_profiles',
]);
}
}
class ProfilesTable extends Table
{
public function initialize(array $config)
{
$this->belongsToMany('Reservations', [
'joinTable' => 'reservation_profiles',
]);
}
}
Then as you can see ReservationsTable is the model that relate Reservation to Profiles using reservation_profiles table to do that Profiles does the same. You don't need to have a model for reservation_profiles, but you must have this table on your databse, I sugest you to use migration to create them. Finally in your controller, this could be in your ReservationController you call
$this->Reservation->Profiles->find()->where(['condition'=> 'param']);
This may solve your problem, explainning ($this) refer to the Controller class, ->REservation refer to the model Reservation -> Profiles refer to related model to the class Reservation model, ->find()... refer to the query executed. If you need more https://book.cakephp.org/3.0/en/orm/associations.html#belongstomany-associations
How can I save multiple images in the secondary table?
My first table is Car, which has the fields:
id
title
featured_image and
My secondary table is Gallery, which has the fields:
id
car_id
gallery_images
Model Car.php
class Car extends AppModel
{
var $name='Car';
var $hasOne = array(
'Gallery' => array('className' => 'Gallery',
'foreignKey' => 'car_id'
));
}
Model Gallery.php
class Gallery extends AppModel
{
var $name='Gallery';
var $belongsTo = array(
'Car' => array('className' => 'Car',
'foreignKey' => 'car_id',
)
);
}
CarController.php
$this->Car->saveAll($this->data)
To save multiple images associated to a car, first you have to modify your Car model to:
class Car extends AppModel
{
var $hasMany = array(
'Gallery' => array(
'className' => 'Gallery',
'foreignKey' => 'car_id'
));
}
or just
class Car extends AppModel
{
var $hasMany = array('Gallery'); //simplified version, as fields follow convention
}
Then you have to structure you data as follows:
$data = array(
'Car' => array('title' => 'Volvo'),
'Gallery' => array(
array('gallery_images' => '/path/image1'),
array('gallery_images' => '/path/image1'),
array('gallery_images' => '/path/image3'),
),
);
In your view, the form should have the following structure:
echo $this->Form->create('Car', array('action' => 'add'));
echo $this->Form->input('Car.title');
echo $this->Form->input('Gallery.0.gallery_images');
echo $this->Form->input('Gallery.1.gallery_images');
echo $this->Form->input('Gallery.2.gallery_images');
echo $this->Form->end();
In your CarControllers::add() you can save all four records (one car and three images) with:
$this->Car->saveAll($this->request->data);
Please consider renaming your fields to represent better your data. For example, gallery_images should be renamed to something like image_filename, and model Gallery to Image of Photo.
As per Car.featured_image, it can hold a foreign key for galleries, or you can move this field on to the galleries/images table and make it a field named type (holding 'featured, normal, etc'), or perhaps a boolean named is_featured.
I have the following code setup (snipped for brevity)
class BasePackage extends AppModel {
public $name = 'BasePackage';
public $hasAndBelongsToMany = array('ProductSubtype', 'ProductType');
}
class ProductType extends AppModel {
public $name = 'ProductType';
}
class ProductSubtype extends AppModel {
public $name = 'ProductSubtype';
}
Above are the simple Model classes.
/* tables in database */
base_packages
product_types
product_subtypes
base_packages_product_types
base_packages_product_subtypes
The first table is the main package that users are creating with the form, the product_* tables are pre-loaded with appropriate types and subtypes (they don't change very often), the last two are the Join tables that CakePhp wants to have
/* in BasePackage/add.ctp */
// ...
<ul class="nwblock">
<li>
<?php
echo $this->Form->input('ProductType.product_type_id', array(
'label' => 'Choose Product Type',
'type' => 'select',
'class' => 'form-control',
'style' => 'width:300px; margin-bottom:20px;',
'options' => $protypes
));
?>
</li>
</ul>
<ul class="nwblock">
<li>
<?php
echo $this->Form->input('ProductSubtype.product_subtype_id', array(
'label' => 'Choose Subtype(s)',
'multiple' => 'multiple',
'type' => 'select',
'class' => 'form-control',
'style' => 'width:300px;height:390px;margin-bottom:20px;',
'options' => $subtypes
));
?>
</li>
</ul>
// ...
Above we see the two controls that are loaded from the product_* tables. The types are a single select dropdown and the subtypes are a multiple select list.
/* in BasePackageController.php */
public function add() {
$protypes = $this->BasePackage->ProductType->find('list',
array('fields' => array('ProductType.id', 'ProductType.display')));
$subtypes = $this->BasePackage->ProductSubtype->find('list',
array('fields' => array('ProductSubtype.id', 'ProductSubtype.display')));
$this->set('protypes', $protypes);
$this->set('subtypes', $subtypes);
if ($this->request->is('post')) {
$this->BasePackage->create();
if (!empty($this->request->data)) {
$this->BasePackage->saveAll($this->request->data, array('deep' => true));
}
}
}
The process is as follows, while the user creates a new BasePackage, they select a ProductType from a dropdown box and one to many ProductSubtypes from a multiple select list. When the $this->BasePackage->saveAll() call is made, the data to be inserted into base_packages and base_packages_product_types tables is inserted correctly. However, the base_packages_product_subtypes table remains untouched.
UPDATE:
If I remove the 'multiple' => 'multiple', from the form->input options, the code saves both the producttype and the productsubtype (as expected). This is obviously not sufficient, as I need to save 1-to-many. Anyone know how to activate the 'Many' part of the HABTM?
To me BasePackage <> ProductType looks more like it should be a many-to-one relation, ie BasePackage belongsTo ProductType?
Anyways... please follow the conventions as described in the Cookbook:
http://book.cakephp.org/2.0/en/models/saving-your-data.html#saving-related-model-data-habtm
The form helper should be fed with the model name, ie ProductSubtype, and the view var should be camel backed plural, ie productSubtypes, that way CakePHP will do the rest for you automatically.
public function add() {
// ...
$this->set('productSubtypes', $subtypes);
// ...
}
echo $this->Form->input('ProductSubtype', array(
'label' => 'Choose Subtype(s)',
'class' => 'form-control',
'style' => 'width:300px;height:390px;margin-bottom:20px;'
));
Can you try with BasePackage->saveAssociated ?
http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-saveassociated-array-data-null-array-options-array
I'm building an MMA (mixed martial arts) website with CakePHP. I've got a fights table in my database that has three columns at its simplest: id, fighter_a, and fighter_b.
I'm having trouble getting my head around what type of relation my Fight model would have with my Fighter module. Am I right in thinking fighter_a and fighter_b would be two hasOne relations?
I tried this with the following in my Fight model:
<?php
class Fight extends AppModel {
public $name = 'Fight';
public $hasOne = array(
'FighterA' => array(
'className' => 'Fighter',
'foreignKey' => 'fighter_a'
),
'FighterB' => array(
'className' => 'Fighter',
'foreignKey' => 'fighter_b'
)
);
}
And then this in my Fighter model:
<?php
class Fighter extends AppModel {
public $name = 'Fighter';
public $hasMany = array(
'Fight'
);
}
But this threw an error in my CakePHP application when calling $this->Fight->findById($id) (where $id was the ID of a fighter):
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Fight.fighter_id' in 'field list'
How can I link my models so that I can call all fights a fighter has been in?
Distilled from conversation under the question, the solution would be this:
Rewrite the $hasMany in your FighterModel to look like:
public $hasMany = array(
'Fight' => array(
'className' => 'Fight',
'finderQuery' => 'SELECT * FROM fights AS Fight WHERE Fight.fighter_a_id = {$__cakeID__$} OR Fight.fighter_b_id = {$__cakeID__$};'
)
);
I have two issues with my CakePHP application (its my first one in CakePHP). I am trying to convert an old php website to cake.
1.Issue
I have my controller that accepts a parameter $id, but the data is comming from joined tables so in the cookbook it had something like this
MY Controller
dish_categories_controller.php
class DishCategoriesController extends AppController {
var $uses = array("DishCategory");
var $hasOne ='';
function get_categories($id)
{
$this->set('dishes',$this->DishCategory->find());
$this->layout = 'master_layout';
}
}
model
dish_category.php
class DishCategory extends AppModel{
var $name = 'DishCategory';
var $hasOne = array(
'Dish' => array(
'className' => 'Dish',
'conditions' => array('Dish.id' => '1'),
'dependent' => true
)
);
}
As you can see the Dish.id=> '1' is hard coded, how can make it dynamic there so that I pass a value and make it something like Dish.if =>$id ?.
So that was my first issue.
The second issue is related to the view
That model returns only one record, how can I make it so that it returns all and also how would I be able to loop through that, below the code in the view currently and the array format.
This is in my view
<?php
echo $dishes['DishCategory']['category_info'];
echo $dishes['DishCategory']['category_title'];
echo $dishes['Dish']['dish_name'];
echo $dishes['Dish']['dish_image'];
echo $this->Html->image($dishes['Dish']['dish_image'], array('alt' => 'CakePHP'))
?>
Array Format
Array ( [DishCategory] => Array
( [id] => 1 [category_name] => Appetizers
[category_keywords] => appetizer, appetizers
[category_title] => Our Side Dishes
[category_info] => Test Test
[dish_id] => 1 )
[Dish] => Array ( [id] => 1
[dish_name] => Rice
[dish_disc] => The Best flavor ever
[dish_price] => 2.90 [dish_image] => /img/rice_chicken.jpeg [dish_category_id] => 1
[dish_price_label] => Delicious Arepa ) )
I would appreciate your help to help me understand how to better do this. Thank you.
Firstly, you DishCategoriesController has model properties, you can remove them. In your controller, you will set up the conditions for the find like so:
class DishCategoriesController extends AppController {
function get_categories($id)
{
// find category with a dish of $id
$this->set('dishes', $this->DishCategory->find('all', array(
'conditions' => array(
'Dish.id' => $id
)
)));
// set master layout
$this->layout = 'master_layout';
}
}
Your DishCategory model will look very basic, you don't need to hard code the relationship condition:
class DishCategory extends AppModel {
/**
* hasOne associations
*
* #var array
*/
public $hasOne = array(
'Dish' => array(
'className' => 'Dish',
'foreignKey' => 'dish_category_id'
)
)
}
At this point it is worth noting that since the DishCategory hasOne Dish, using the above find query, it will only ever return a single result. But, it you were returning multiple results, you could loop through them in your view like so:
<?php foreach ($dishes as $key => $dish): ?>
<?php var_dump($dish) ?>
<?php endforeach ?>