How to contain unassociated entities in CakePHP? - php

Sample query:
TableRegistry::getTableLocator()
->get('Parents')
->find()
->contain([
'Children' => function (Query $query) {
return $query->where([
'Children.code = Parent.code'
]);
}
])
Parent and Children tables only have code as common field.
How do I define their association?
How do I contain unassociated entities?

You can customize the foreignKey and bindingKey in association configuration
In ParentsTable.php:
$this->hasMany('Children', [
'bindingKey' => 'code',
'foreignKey' => 'code'
]);
This config will set which fields to look for when associating entities.
Then you could associate entities on your controller like this:
// This query will contain children where Children.code === Parent.code
TableRegistry::getTableLocator()
->get('Parents')
->find()
->contain('Children');

You can define the relationship between those tables througth ParentesTable as #kgbph explained.
Although in your exemple I think the better solution is use Cake's TreeBehavior. Here's the link to the documentation.

Related

Is it possible to use find('list') with Containable behavior in CakePHP 3?

Application is using CakePHP version 3.5.18
I have used the ORM to obtain a list which returns key/value pairs:
$Displays = TableRegistry::get('Displays');
$displays = $Displays->find('list', ['keyField' => 'id', 'valueField' => 'id'])->where(...)->toArray();
This works as expected. An example of debug($displays); looks like this:
(int) 36 => (int) 36,
(int) 160 => (int) 160,
(int) 149 => (int) 149,
...
In this particular application there are 3 Models which work in the following hierarchy:
Regulations (Level 1. Table name regulations)
Groups (Level 2. Table name groups)
Displays (Level 3. Table name displays)
The application has been baked so that the appropriate relationships are defined in the Table classes.
// src/Model/Table/RegulationsTable.php
$this->hasMany('Groups', [
'foreignKey' => 'regulation_id'
]);
// src/Model/Table/GroupsTable.php
$this->hasMany('Displays', [
'foreignKey' => 'group_id'
]);
// src/Model/Table/DisplaysTable.php
$this->belongsTo('Groups', [
'foreignKey' => 'group_id',
'joinType' => 'INNER'
]);
What I want to do is adjust the query defined by $displays so that the results correspond to a given array of Regulations.id. The vanilla SQL equivalent of what I'm trying to do is ... WHERE regulations.id IN (1,2,3) assuming the ID's were 1, 2 and 3.
When I read the Cake docs on Passing conditions to contain it says:
When you limit the fields that are fetched from an association, you must ensure that the foreign key columns are selected. Failing to select foreign key fields will cause associated data to not be present in the final result.
I don't understand how this is possible with find('list') because that only selects the keyField and valueField specified by the developer? In this case that would be displays.id for both.
In any case I don't understand how to write this query because in the same linked documentation it says
When using contain() you are able to restrict the data returned by the associations and filter them by conditions. To specify conditions, pass an anonymous function that receives as the first argument a query object, \Cake\ORM\Query
What would the contain() actually operate on: I assume it's Regulations but don't know how to write this.
I think you need to look at filtering data by matching, not through contains. (If I understand correctly.)
You can combine find('list') with ->matching(...):
$displays = $this->Displays->find('list')
->matching('Groups.Regulations', function ($q) use ($ids) {
return $q->where(['Regulations.id IN' => $ids]);
})
->toArray();
(Still untested)
Filtering by associated data

how to insert the pivot relationship in laravel

consider i have 2 models that have the pivot relationship many to many between them . now when i want to insert the pivot table how can i achieve it currently i am doing this :
DB::table('model1_model2')
->insert([
'something' => $something,
'something2' => $something2,
]);
and i kinda feel that its not right and i have do save it with some relation ship or sync !! any idea how to do this ?
EDIT‌:
Added relationship
public function accommodationRoom()
{
return $this->belongsToMany(AccommodationRoom::class)->withPivot('guest_first_name','guest_last_name','guest_cell_phone','guest_nationality_id');
}
As mentioned in the Inserting & Updating Related Models - Many To Many Relationships documentation:
When attaching a relationship to a model, you may also pass an array of additional data to be inserted into the intermediate table:
$model1->accommodationRoom()->attach($accommodationRoomId, [
'something' => $something,
'something2' => $something2,
]);
If you're attaching multiple relations then you would would pass a multidimensional array with the keys are the id of the relation and the values are the arrays of additional data:
$model1->accommodationRoom()->attach( [
1 => ['something' => $something, 'something2' => $something2,],
3 => ['something' => 'something else', 'something2' => $something2,],
]);
The same is true for the sync() method as well.

Junction table in yii php

I would like to create a junction table tbl_guid_cost_centre that gets taken care of without me manually saving it to the database. I tried adding this to my relations:
'costCentre' => [
self::HAS_ONE,
'CostCentre',
'guid_to',
'foreignKey' => 'guid',
'tbl_guid_cost_centre(guid_to, cost_center_id)',
"order" => "id desc"],
so that my when saving the costCentre, a row is created for it in my tbl_guid_cost_centre. However I'm getting the error:
Property "CHasOneRelation.0" is not defined.
Any suggestion?
You can have your junction table with the keyword through in your relations:
public function relations() {
'guidCostCentre' => [
self::HAS_ONE,
'GuidCostCentre',
['guid_to' => 'guid']
],
'costCentre' => [
self::HAS_ONE,
'CostCentre',
'cost_centre_id',
'through' => 'guidCostCentre'
]
}
You're defining HAS_ONE relation in a wrong way. The first three elements of relation configuration array should be: relation type, related model name and foreign keys definition. All further elements should be indexed by keys related to relation properties. 'tbl_guid_cost_centre(guid_to, cost_center_id)', probably generates this error, because it does not have a key, so it is treaded as a value for 0 property. You didn't share any details, so it is hard to guess what you want to achieve, but you should start from something like this:
'costCentre' => [
self::HAS_ONE,
'CostCentre',
'guid_to',
'order' => 'id desc',
],
And add additional settings at the end array with correct key.
Some examples and explanation you can find in the documentation: https://www.yiiframework.com/doc/guide/1.1/en/database.arr#declaring-relationship

HasOne and HasMany for the same Table cakephp

i'd like to set hasOne and hasMany to same model,in a part of my code i need only 1 result, but in other part i need all result (Objects from type Client that will return for a table in my site):
$this->hasOne('Vendas')
->setForeignKey('id_cliente')
->setBindingKey('id')
;
$this->hasMany('Vendas')
->setForeignKey('id_cliente')
->setBidingKey('id');
This is possible, or i made a mistake?
Read the manual https://book.cakephp.org/3.0/en/orm/associations.html. Read the whole page carefully.
className: the class name of the table being associated to the current model. If you’re defining a ‘User hasOne Address’ relationship, the className key should equal ‘Addresses’.
conditions: an array of find() compatible conditions such as ['Addresses.primary' => true]
Define the class name and conditions you need for your assocs.
$this->hasOne('Foo', [
'className' => 'Foo',
'conditions' => [/* whatever you need*/]
]);
$this->hasMany('Bar', [
'className' => 'Foo',
'conditions' => [/* whatever you need*/]
]);
If the relationship is 1-* you should define the relationship as hasMany(). Then you write a query for one result and a query for multiple results

Zf2 HydratingResultSet and Multiple Entities

Just was wondering if ZF2's hydrating resultset can hydrate multiple entities. Consider the snippet below:
$sql = new Sql($this->adapter);
$sqlObject = $sql->select()
->from([
'ART' => 'acl_roles'
])
->join([
'ARTT' => 'acl_role_types',
],
'ART.type_id = ARTT.id',
[
'ARTT.id' => 'id',
'ARTT.identifier' => 'identifier',
'ARTT.name' => 'name',
'ARTT.status' => 'status',
'ARTT.dateAdded' => 'date_added',
],
Select::JOIN_INNER
)
->where([
'ART.identifier' => $identifier,
])
->columns([
'ART.id' => 'id',
'ART.type_id' => 'type_id',
'ART.identifier' => 'identifier',
'ART.name' => 'name',
'ART.status' => 'status',
'ART.description' => 'description',
'ART.dateAdded' => 'date_added',
]);
Now if the query was on a single entity, I could do something like:
$stmt = $sql->prepareStatementForSqlObject($sqlObject);
$resultset = $stmt->execute();
if ($resultset instanceof ResultInterface && $resultset->isQueryResult()) {
$hydratingResultSet = new HydratingResultSet(new ArraySerializable, new EntityClass);
$hydratingResultSet->initialize($resultset);
return $hydratingResultSet->current();
}
However in my case I need the hydrating result set to be able to build and return multiple entities (namely AclRoleEntity and AclRoleTypeEntity). Is this something that is possible? If yes how (considering the result set being a flat array of combination of both entities). If no are there better alternatives to achieve this without using Doctrine/Propel?
Thanks
It's totally possible, you're just going to need a configured (possibly custom) Hydrator.
Your hydrator will need to know the logic to inject your parameters into your objects from a flat array, and how to reduce your object models back to a flat array on extraction.
You're probably looking at a few Hydrator Strategies or a hydrator naming strategy and potentially a combination of both.
With the correct hydrator, you can achieve what you're looking for.

Categories