How to make relationships in cakephp3 - cakephp

I am trying to make the relationship between the tables, but can’t figure out what I doing wrong.
I read The documentations in cakephp3, and tons of post in stackoverflow, and still can’t get any result.
--------------------------
name |invoices |shares |
var |id |id |
var | |idBill |
--------------------------
The relationship should be from idBill in shares and id in invoices
class SharesTable extends Table {
public function initialize(array $config) {
parent::initialize($config);
$this->belongsTo('invoices', [
'className' => 'App\Model\Table\InvoicesTable',
'foreignKey' => 'idBill',
'targetForeignKey' => 'id'
]);
}
}
And in controller try to print the bill associate to the share like this:
public function view($id = null)
{
$share = $this->Shares->get($id, [
'contain' => []
]);
var_dump( $share->invoices );
$this->set('share', $share);
}
I just expect to print the bill but I always get null

Your contain is empty, so you won't receive any associations. Also the default entity property name for belongsTo (and hasOne) associations is the singular, underscored variant of the association name, ie invoice, not invoices.
It's also recommended to use association names that do match the table alias, that is Invoices (with a capital I) for InvoicesTable, that way it will find the class automatically and you don't have to specify it via the className option (also there is no targetForeignKey option for belongsTo associations).
$this->belongsTo('Invoices', [
'foreignKey' => 'idBill'
]);
$share = $this->Shares->get($id, [
'contain' => ['Invoices']
]);
debug($share->invoice);
See also
Cookbook > Database Access & ORM > Retrieving Data & Results Sets > Eager Loading Associations Via Contain
Cookbook > Database Access & ORM > Associations - Linking Tables Together

Related

Saving / Creating data to associated table is not working

I have three table,
Authors
Stories
Details
Stories and Details are associated with the Authors, with hasOne relationship.
Bellow is the code in my AuthorsController
function add(){
if($this->request->is('POST')){
$author = $this->Authors->newEntity($this->request->data,
['associated' => ['Stories', 'Details'], 'validate' => false]
);
$this->Authors->save($author);
}
}
This is only saving data in the Authors table, not in the other two table.
Bellow is the data I have in $this->request->data
'Authors' => [
'name' => 'Bikash'
'Age' => '22'
'stories' => [
'name' => 'Life Without the L'
'gener_id' => 5
]
'details' => [
'qualification' => 'XYZ'
'biography'
]
];
What I am missing?
You are using the wrong property names, stories and details won't work.
The default association property name for hasOne associations, is the underscored singular variant of the association alias, so for Stories that would story, and for Details it would be detail.
See also
Cookbook > Database Access & ORM > Associations - Linking Tables Together > HasOne Associations
Inflector Sandbox > Stories
Inflector Sandbox > Details

cakephp: how to display foreign key table field

basically,
database table:
Products: id, name
Comments: productId,comment
Model:
product.php
class Product extends AppModel {
public $hasMany = array('Comment'=>array('foreignkey'=>'productId'));
}
comment.php
class Comment extends AppModel {
public $belongsTo = array('Product');
}
In product index.ctp, how can I display one product comment? What I need to write in ProductsController.php and index.ctp?
If you follow the CakePHP conventions (CakePHP Convention over Configuration) then all of this will be done automagically for you, it requires foreign keys to be named product_id rather than productId (although you have setup the foreign key in the relationship - it is just easier to start from the beginning following the conventions).
You should also specify the class name in the relationship:
public $hasMany = array(
'Comment' => array(
'className' => 'Comment',
'foreignKey' => 'product_id'
)
);
In your case what you should do in the controller is:
$products = $this->Product->find('all');
This will fetch all your products and any associated comments on those Products (and also any other associated models you have declared in the Product Model)
If you want to read more about setting this up you can check out CakePHP - Retrieving your data

Save associations with Cakephp 3

I have a problem with CakePHP 3 and saving new entity and its associations with one operation.
In my opinion I do it like recommended in the documentation.
Here my controller:
$articles = TableRegistry::get('Articles');
$article = $articles->newEntity($this->request->data);
if ($this->request->is('post')) {
$valid = $articles->validate($article, [
'associated' => ['Topics']
]);
if ( $valid ) {
$articles->save($article, [
'validate' => false,
'associated' => ['Topics']
]);
}
}
Thats my models:
class ArticlesTable extends Table {
public function initialize(array $config) {
$this->primaryKey('article_id');
$this->belongsTo ( 'Topics', [
'targetForeignKey' => 'topic_id'
]);
}
}
class TopicsTable extends Table {
public function initialize(array $config) {
$this->primaryKey('topic_id');
$this->hasMany ( 'Articles', [
'targetForeignKey' => 'article_id'
]);
}
And this is my database:
CREATE TABLE `articles` (
`article_id` int(11) NOT NULL AUTO_INCREMENT,
`topic_id` int(11) DEFAULT NULL,
PRIMARY KEY (`article_id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=latin1;
CREATE TABLE `topics` (
`topic_id` int(11) NOT NULL AUTO_INCREMENT,
`topic_title` varchar(45) DEFAULT NULL,
PRIMARY KEY (`topic_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
MY form looks like that:
<input type="text" id="articles-heading" maxlength="45" required="required" name="Articles[heading]">
<input type="text" id="articles-topics-topic-title" list="topics" name="Articles[Topics][topic_title]">
I want to create a form which contains all the associations and save it with one request.
Missing columns
There is no column named heading in your articles table.
Invalid foreign key configuration
Your configuration looks wrong, targetForeignKey is for BelongsToMany associations, and article_id (the primary key of the other table) is not the foreign key in an Topic hasMany Comment association, it's topic_id, and it's not necessary to specify it when you're following the naming conventions.
So when specifiying the foreign keys at all, it should look something like this:
class ArticlesTable extends Table {
public function initialize(array $config) {
$this->primaryKey('article_id');
$this->belongsTo ( 'Topics', [
'foreignKey' => 'topic_id'
]);
}
}
class TopicsTable extends Table {
public function initialize(array $config) {
$this->primaryKey('topic_id');
$this->hasMany ( 'Articles', [
'foreignKey' => 'topic_id'
]);
}
}
You could avoid a lot of confusion if you'd stick to the convention and name your primary keys id.
See also
Cookbook > Models > Table Objects > Basic Usage
Cookbook > Models > Table Objects > HasMany Associations
Possibly missing accessibility configuration
Since you are trying to save non-default fields (that is Topic data instead of Topic primary key), you'll have to make the field accessible for mass assignment via newEntity. This is possible for example via the Entitiy::$_accessible property, which is permanent for every single instance of that entity until overriden at runtime
class Article extends Entity {
protected $_accessible = [
// ...
'topic' => true
];
}
or by using the accessibleFields option for Table::newEntity(), which only applies to that one single instance.
$article = $articles->newEntity($this->request->data, [
'accessibleFields' => [
'topic' => true
]
]);
See also
Cookbook > Models > Entities > Mass Assignment
API > Cake\ORM\Table::newEntity()
Field Naming Conventions
Finally your form data is incorrectly formatted, names should by default be lowercase and underscored in order to match the entities properties, and they should be singular (unless it's a hasMany or belongsToMany association), ie it should be article and topic instead of Articles and Topics, also it's actually not necessary to use article at all.
echo $this->Form->input('heading');
echo $this->Form->input('topic.topic_title');
<input type="text" name="heading">
<input type="text" name="topic[topic_title]">
See also Cookbook > Helpers > FormHelper > Field Naming Conventions

Has Many relationship how to

I have this model and controller and it returns fine if it is a hasOne relationship but now DishCategory hasMany dishes. When I change the code below to hasMany it gives me an error saying Dish.id is not a known column while if it is a hasOne it works fine. How can I make it so that it returns all the Dish.id's that have id=1? (still using the join).
class DishCategory extends AppModel{
public $hasOne = array(
'Dish' => array(
'className' => 'Dish',
'foreignKey' => 'dish_category_id'
)
);
}
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';
}
}
hasOne relations can be joined into the primary SQL query. hasMany relations cannot and need to be queried in a separate query. The conditions you specify for the query only apply to the primary query, all separate relational queries are build just based on the ids retrieved in the primary query. Set debug to 2 and have a good look at the query log to see what I mean.
To find the category of a dish, fetch the dish and look at its related category record:
$dish = $this->Dish->find('first', array('conditions' => array('Dish.id' => $id)));
echo $dish['DishCategory']['name'];
Since I take it that a dish belongsTo one category, your query "find all categories which have a dish with id x" makes little sense; there should only be one anyway.

CakePHP multi-model view

I am creating a website in CakePHP and I am kind of new on it. I couldn't find good resources on this matter, so there you go:
I have a three table structure for registering users: Users, Addresses and Contacts. I have to build a view with info of all three tables like:
Full Name: [ ] (from Users)
Shipping Address: [ ] (from Address)
Mobile Phone: [ ] (from Contact)
e-Mail Address: [ ] (from Contact)
What is the best way to deal with this situation. Specially for saving. Creating a new Model to represent this, that will have a save() method itself (Maybe a sql view in the database) Create a Controller to deal with this View that binds or unbinds info
I wonder still how I will handle both contacts as they will be 2 different INSERT's
Any hint or resources I can dig of I will be glad.
If your using the latest 1.2 code, check out Model::saveAll in the api
eg. Your view might look something like this:
echo $form->create('User', array('action' => 'add');
echo $form->input('User.name');
echo $form->input('Address.line_1');
echo $form->input('Contact.tel');
echo $form->end('Save');
Then in your Users controller add method you'd have something like:
...
if($this->User->saveAll($this->data)) {
$this->Session->setFlash('Save Successful');
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash('Please review the form for errors');
}
...
In your User model you will need something like:
var $hasOne = array('Address', 'Contact');
Hope that helps!
http://api.cakephp.org/class_model.html#49f295217028004b5a723caf086a86b1
CakePHP allows you to easily maintains link between your models using relationship, see http://book.cakephp.org/view/78/Associations-Linking-Models-Together for the complete detail. Then retreiving the right User, you'll also get "for free", its address and contact informations.
3 models : User, Address, Contact
User hasOne Address, Contact
Address belongsTo User
Contact belongsTo User
in your model you define this like this :
class User extends AppModel {
var $name = 'User';
var $hasOne = array('Address','Contact');
..
To make this view, you need user_id field ind addresses, and contacts tables
To use this in a view, you simply call a find on the User model with a recursive of one (and btw, the users controller only uses User model).
$this->User->recursive = 1;
$this->set('user', $this->User->find('first', array('conditions'=>array('id'=>666)));
This will result in this array for your view :
array(
'Use' => array(
'id' => 666,
'name' => 'Alexander'
),
'Address' => array(
'id' => 123,
'zip' => 555
),
'Contact' => array(
'id' => 432,
'phone' => '555-1515'
));

Resources