cakephp 4 find method with contain is skipping some records from subtable - relationship

I have master table portfolios and there are 2 child tables portfoliotags and p_snaps
models are as below
class PortfoliosTable extends Table
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('portfolios');
$this->setDisplayField('id');
$this->setPrimaryKey('id');
$this->hasMany('PSnaps', [
'foreignKey' => 'portfolio_id',
]);
$this->hasMany('PortfolioTags', [
'foreignKey' => 'portfolio_id',
]);
}
}
class PortfolioTagsTable extends Table
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('portfolio_tags');
$this->setDisplayField('id');
$this->setPrimaryKey('id');
$this->belongsTo('Portfolios', [
'foreignKey' => 'portfolio_id',
'joinType' => 'INNER',
]);
$this->belongsTo('Tags', [
'foreignKey' => 'tag_id',
'joinType' => 'INNER',
]);
}
}
class PSnapsTable extends Table
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('p_snaps');
$this->setDisplayField('title');
$this->setPrimaryKey('id');
$this->belongsTo('Portfolios', [
'foreignKey' => 'portfolio_id',
'joinType' => 'INNER',
]);
}
}
find() method in controller is as below
$pfolios = $this->Portfolios->find('all')
->select(['Portfolios.id','Portfolios.client','Portfolios.country','Portfolios.url'])
->where(['Portfolios.status'=>1])
->order(['Portfolios.order_at'=>'asc','Portfolios.id'=>'asc'])
->limit(8)
->contain([
'PSnaps'=>function($q){
return $q
->select(['PSnaps.portfolio_id','PSnaps.snap'])
->where(['PSnaps.status'=>1])
->order(['PSnaps.order_at'])
->limit(1);
},
'PortfolioTags.Tags'=>function($q2){
return $q2
->order(['Tags.tag']);
}
])
->toList();
debug($pfolios);exit;
It is retuning PSnaps inside first record of Portfolio, while in all other records it is empty array
while I know there are records in database I tried using below query in mysql too
SELECT p.id,s.snap FROM `portfolios` p INNER join p_snaps s on p.id=s.portfolio_id
it is returning records as below
id snap
1 s1.png
2 pers.png
3 gmap.png
4 ita.png
5 soapd.png
6 chat.png
7 aissmo.png
8 zippy.png
2 pereport.png

This is because of limit() inside PSnaps, if it is commented it will show all PSnap records under each Portfolios records.
To handle this situation I fix it by adding hasOne relation in PortfoliosTable.php as
$this->hasOne('FirstPSnaps', [
'className' => 'PSnaps',
'foreignKey' => 'portfolio_id',
'strategy' => 'select',
'sort' => ['FirstPSnaps.order_at' => 'ASC'],
'conditions' => function ($e, $query) {
return [];
}]);
now call in controller will be changed to as below
$pfolios = $this->Portfolios->find('all')
->select(['Portfolios.id','Portfolios.client','Portfolios.country','Portfolios.url'])
->where(['Portfolios.status'=>1])
->order(['Portfolios.order_at'=>'asc','Portfolios.id'=>'asc'])
->limit(8)
->contain([
/********instead of PSnap I used FirstPSnaps *********/
'FirstPSnaps'=>function($q){
return $q
->select(['FirstPSnaps.portfolio_id','FirstPSnaps.snap'])
->where(['FirstPSnaps.status'=>1])
->order(['FirstPSnaps.order_at']);
},
'PortfolioTags.Tags'=>function($q2){
return $q2
->order(['Tags.tag']);
}
])
->toList();
debug($pfolios);exit;

Related

Struggling to save in 2 tables with hasOne association with cakePHP 3

I’m struggling with the hasOne association. Here are my 2 tables :
Actually the ‘Etudiant’ (means student) has one User associated to it with the foreign key id present in both table which is the primary key of Users.
Here is the model EtudiantTable (student) :
class EtudiantTable extends Table
{
/**
* Initialize method
*
* #param array $config The configuration for the Table.
* #return void
*/
public function initialize(array $config)
{
parent::initialize($config);
$this->setTable('etudiant');
$this->setDisplayField('ETUDIANT_ID');
$this->setPrimaryKey('ETUDIANT_ID');
$this->addBehavior('Timestamp');
$this->belongsTo('Maitredestage', [
'foreignKey' => 'MAITREDESTAGE_ID',
'joinType' => 'INNER'
]);
$this->belongsToMany('Tuteuriut', [
'foreignKey' => 'ETUDIANT_ID',
'targetForeignKey' => 'TUTEURIUT_ID',
'joinTable' => 'Suivre'
]);
$this->belongsTo('Users', [
'foreignKey' => 'ID',
'joinType' => 'INNER'
]);
}
And here is the add function in the EtudiantController :
public function add()
{
$etudiant = $this->Etudiant->newEntity();
if ($this->request->is('post')) {
$etudiant = $etutable->patchEntity($etudiant, $this->request->data(), [
'associated' => ['Users']
]);
if ($this->Etudiant->save($etudiant)){
$this->Flash->success(__('The etudiant has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The etudiant could not be saved. Please, try again.'));
}
$this->set(compact('etudiant'));
}
And my usersTable model is like this :
$this->belongsTo('Users', [ 'foreignKey' => 'ID', 'joinType' => 'INNER' ]);
I want to do this : when i insert an Etudiant it inserts a new user and both have the same id.
But currently when i insert an Etudiant i get an error saying “no default value…id is empty” but the id is supposed to be inserted automatically.
I'm completely stuck so i need your help. Thanks in advance.

How to change the join type of a contained association per find() call?

How to reset the type of joins in different places?
Here are my tables:
Tenancy Table
class TenancyTable extends Table
{
/**
* Initialize method
*
* #param array $config The configuration for the Table.
* #return void
*/
public function initialize(array $config)
{
parent::initialize($config);
$this->table('tenancy');
$this->displayField('id');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->belongsTo('Properties', [
'foreignKey' => 'property_id',
'className' => 'property',
'joinType' => 'INNER'
]);
Property Table
class PropertyTable extends Table
{
/**
* Initialize method
*
* #param array $config The configuration for the Table.
* #return void
*/
public function initialize(array $config)
{
parent::initialize($config);
$this->table('property');
$this->displayField('id');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->hasMany('Tenancies', [
'foreignKey' => 'property_id',
'className' => 'tenancy',
'joinType' => 'LEFT'
]);
For example change the Tenancy join type to Right
$setting = [
'contain' => [
'Tenancies' => [
'joinType' => 'RIGHT'
]
]
];
$properties = $this->find('all', $setting)
->hydrate(false)
->select(['Property.id', 'Property.company_id', 'Property.address1', 'Property.postcode'])
You would do it exactly the way you are showing it, ie by specifying the joinType option in contain settings.
However, since you are querying from the Property table, this will have no effect, as hasMany associations are being retrieved in a separate query, so no joins involved. It would work for hasOne and belongsTo associations, which are being retrieved in the same query.

CakePHP 3 - Users belongsToMany Users

I have a specific request, to build an association between users. This causes me confusion, how to reduce duplicate associations, query and results?
The starting point would look like this?
// UsersTable
$this->belongsToMany('Users', [
'through' => 'Connections',
]);
How to fetch all associations in one query, regardless of whether users key in "user_from" or "user_to" field?
How about using aliases?
Your users table:
class UsersTable extends Table
{
public function initialize(array $config)
{
$this->hasMany('ToConnections', [
'className' => 'Connections',
'foreignKey' => 'user_to'
]);
$this->hasMany('FromConnections', [
'className' => 'Connections',
'foreignKey' => 'user_from'
]);
}
}
And your connections table:
class ConnectionsTable extends Table
{
public function initialize(array $config)
{
$this->belongsTo('ToUsers', [
'className' => 'Users',
'foreignKey' => 'user_to'
]);
$this->belongsTo('FromUsers', [
'className' => 'Users',
'foreignKey' => 'user_from'
]);
}
}
You can then use contain() to load associated models as required.
$query = $conections->find()->contain([
'ToUsers',
'FromUsers'
]);
$recipients = TableRegistry::get('users');
$query = $recipients->find()->contain([
'ToConnections.FromUsers',
]);

cakephp3 condition based associations not works

I am using hasOne association.Here my code for UserMastersTable :
class UserMastersTable extends Table {
public function initialize(array $config) {
parent::initialize($config);
$this->table('user_masters');
$this->hasOne('PersonMasters', [
'className' => 'PersonMasters',
'foreign_key' => 'user_master_id',
'conditions' => ['PersonMasters.status' => 1],
'dependent' => true,
]);
} }
When use find() in my controller.It fetch all user_masters data and person_masters data whose status ='1'.
but problem is that i already assign condition where association bind..already give condition that only display that data whose person_masters.status=1.
so why it shows all data of user_masters ?
if i give condition in find() in controller then it works fine..
$this->UserMasters->find('all',
['contain' =>
['PersonMasters'],
'conditions' =>
['PersonMasters.status' => 1]
]);
so, how can i globally give condition that only fetch data of user_masters and person_masters where PersonMasters.status=1?
Try this might be it will resolve your issue
$this->UserMasters->find('all',[
'contain' =>
['PersonMasters' => [
'conditions' => ['status' => 1]
]
],
]);

How to save associated joinData in cakephp 3.x

I have Problems and Fields in a many-to-many relationship. The join table fields_problems has a field named fieldvalue I am trying to have a form that will insert a problem record and also multiple records into fields_problems.
/src/Model/Table/ProblemsTable.php
class ProblemsTable extends Table
{
public function initialize(array $config)
{
parent::initialize($config);
$this->table('problems');
$this->displayField('id');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->belongsToMany('Fields', [
'foreignKey' => 'problem_id',
'targetForeignKey' => 'field_id',
'joinTable' => 'fields_problems'
]);
}
...
/src/Model/Table/FieldsTable.php
class FieldsTable extends Table
{
public function initialize(array $config)
{
parent::initialize($config);
$this->table('fields');
$this->displayField('name');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->belongsToMany('Problems', [
'foreignKey' => 'field_id',
'targetForeignKey' => 'problem_id',
'joinTable' => 'fields_problems'
]);
}
...
/src/Model/Table/FieldsProblemsTable.php
class FieldsProblemsTable extends Table
{
public function initialize(array $config)
{
parent::initialize($config);
$this->table('fields_problems');
$this->displayField('id');
$this->primaryKey('id');
$this->belongsTo('Fields', [
'foreignKey' => 'field_id',
'joinType' => 'INNER'
]);
$this->belongsTo('Problems', [
'foreignKey' => 'problem_id',
'joinType' => 'INNER'
]);
}
...
And I want to Add a new problem, link it to fields, and add values to the fieldvalue field in the join table.
So I have this /src/Template/Problems/add.ctp
<div class="problems form large-10 medium-9 columns">
<?= $this->Form->create($problem) ?>
<fieldset>
<legend><?= __('Add Problem') ?></legend>
<?php
echo $this->Form->input("Problems.id");
echo $this->Form->input('Problems.summary');
echo $this->Form->input('Problems.Fields.0._ids', [
'type' => 'select',
'multiple' => false,
'options' => $fields,
]);
echo $this->Form->input('Problems.Fields.0._joinData.fieldvalue');
echo $this->Form->input('Problems.Fields.1._ids', [
'type' => 'select',
'multiple' => false,
'options' => $fields,
]);
echo $this->Form->input('Problems.Fields.1._joinData.fieldvalue');
?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
</div>
And this add() in /src/Controller/ProblemsController.php
public function add()
{
$problem = $this->Problems->newEntity();
if ($this->request->is('post')) {
$problem = $this->Problems->patchEntity($problem, $this->request->data, ['associated'=>['Fields._joinData']] );
//$problem->dirty('fields',true);
if ($this->Problems->save($problem)) {
$this->Flash->success(__('The problem has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The problem could not be saved. Please, try again.'));
}
}
$fields = $this->Problems->Fields->find('list', ['limit' => 200]);
$this->set(compact('problem', 'fields'));
$this->set('_serialize', ['problem']);
}
When I fill out and submit the app form, the Problem record is saved, but the association is not, nothing gets inserted into fields_problems.
What am I doing wrong that is preventing the associated joinData from being saved?
Despite the cookbook (http://book.cakephp.org/3.0/en/views/helpers/form.html) saying to use the special _ids key, don't!
Changing "_ids" to "id" fixed the form and now it functions properly saving the data into the jointable.
Here is the example from the cookbook that I built my app with
echo $this->Form->input('tags.0.id');
echo $this->Form->input('tags._ids', [
'type' => 'select',
'multiple' => true,
'options' => $tagList,
]);
Here is how it should be
echo $this->Form->input('tags.0.id', [
'type' => 'select',
'multiple' => false,
'options' => $tagList,
]);

Resources