I was setup cake php for website: http://bomnuocebara.com but i have error.
I have very basic question but I'm kind of stuck with the syntax here:
I have an entity query. The entity is associated to another table which has a 'name' property? (many to many association)
My $query looks like this when I debug it:
/plugins/MailCalculator/src/Controller/PostalServicesController.php (line 140)
object(MailCalculator\Model\Entity\PostalService) {
'id' => (int) 1,
'carrier' => 'Deutsche Post'
},
'modified' => null,
'_matchingData' => [
'Insurances' => object(MailCalculator\Model\Entity\Insurance) {
'id' => (int) 2,
'name' => 'Wert',
'price' => (float) 4.3,
...
basically the question is, how do I get the values out from the __matchingData array?
I thought the syntax for it should be something like:
$var = $query->insurance->name or $var = $query->insurances['name'] but both things debugged give me 'null'
This should work with your current results.
$var = $query->_matchingData['Insurances']->name
Note, that if you contain association to your query, it should be available also as standard property in result set.
http://book.cakephp.org/3.0/en/orm/query-builder.html#filtering-by-associated-data
The data from the association that is ‘matched’ will be available on
the _matchingData property of entities. If you both match and contain
the same association, you can expect to get both the _matchingData and
standard association properties in your results.
Related
I made myself a plugin for loading related content with beforeFind(), so you could say that ContentPage/10 is similar to ConentNews/10 and Gallery/5.
My table related_contents looks like:
id int(11)
source_table_name varchar(255)
target_table_name varchar(255)
source_table_id int(11)
target_table_id int(11)
My code in behavior:
public function beforeFind(Event $event, Query $query, ArrayObject $options, $primary) {
$attachedTables = self::getAttachedTables(); //Array of ORM models which has this behavior
foreach ($attachedTables as $attachedTable) {
$options = [
'through' => 'RelatedContents',
'bindingKey' => 'RelatedContents.source_table_id',
'foreignKey' => 'RelatedContents.target_table_id',
'conditions' => [
'RelatedContents.source_table_name' => $this->_table->table(),
'target_table_name' => $attachedTable->table(),
]
];
$this->_table->belongsToMany($attachedTable->alias(), $options);
}
}
Now, when i try to find() in my model, zero related entities are found with no error. What am i doing wrong?
If calling your getAttachedTables() method on intialization time causes recursion, then there might be something that needs to be fixed. Reassociating on every find doesn't seem like an overly good idea, unless you really need to do so because the associations have changed/need to change, or have been removed.
That being said, your problem most probably is the missing targetForeignKey option, the bindingKey option that you are using is not available for BelongsToMany associations! Also note that the foreign keys need to be defined without aliases!
Also you're also missing an alias in the conditions, but that shouldn't have anything to do with the problem that no associations are being fetched.
So, foreignKey should be set to the FK column in the current model, and targetForeignKey to the FK column of the target model, like
$options = [
// ...
'foreignKey' => 'source_table_id',
'targetForeignKey' => 'target_table_id',
// ...
];
See also
Cookbook > Database Access & ORM > Associations > BelongsToMany Associations
I'm trying to log each action (insert/update/delete) in the application and I'm doing this by getting the dirty and original values after saving the entity. The problem is that all values of the associated entities are returned as dirty and even is_new flag is set to true but actually I'm updating. What causes this behavior and how can I avoid it?
Example:
$data = [
'name' => $name,
'something' => $something,
'Table1' => [
'id' => $idWhereUpdatingTable1,
'field1' => $field1,
'field2' => $field2,
],
'Table2' => [
'id' => $idWhereUpdatingTable2,
'field3' => $field3,
'field4' => $field4,
],
];
$options = ['associated' => ['Table1', 'Table2']];
$updatedEntity = $this->patchEntity($entity, $data, $options);
$save = $this->save($updatedEntity);
// Successfully logging the changes in the main entity
// Trying to log the changes in the associated entities
foreach($save->table1 as $entity)
{
// everything here is set to dirty (even ID field but it's not an insert) and I'm not able to fetch the updated fields only. Also getOriginal() doesn't return the old values.
}
I did some digging into the dirty() function within an Entity and according to the API if you do not explicitly ask it to check a property then it will just tell you if the Entity has any dirty properties.
So doing
$entity->dirty('title'); Tells you if the tile is dirty but running $entity->dirty(); will just tell you if any property in the entity is dirty.
http://api.cakephp.org/3.1/class-Cake.ORM.Entity.html#_dirty
You may want to make code conditional based on whether or not fields have changed in an entity.
For example, you may only want to validate fields when they change:
// See if the title has been modified. CakePHP version 3.5 and above
$entity->isDirty('title');
// CakePHP 3.4 and Below use dirty()
$entity->dirty('title');
I have both language and language_id fields in my users table. When I want to save my data, save method returns false.
//Table/UsersTable.php
$this->belongsTo('Languages', [
'alias' => 'Languages',
'foreignKey' => 'language_id'
]);
When I remove this code or remove language field from database, save method works properly.
Yeah, remember that CakePHP reserves the a property name where it will store the association data for each of your associations. In Your case, for the Languages association it will use the language property. If you already have a field with the same name, You can configure your association to use another property name:
$this->belongsTo('Languages', [
'alias' => 'Languages',
'foreignKey' => 'language_id',
'propertyName' => 'preferred_language'
]);
Personally, I would stick to conventions and not have a language field in the database, it make very little sense when you already have a language_id field.
I want to populate my database with 'flat' data extracted from an excel sheet. All records are provided as arrays (similar to $request->data) but have their primaryKeys set which values must be kept.
My code:
$imported = 0;
foreach ($data as $record) {
$entity = $table->findOrCreate([$table->primaryKey() => $record[$table->primaryKey()]]);
$entity = $table->patchEntity($entity, $record);
if ($table->save($entity)) {
$imported++;
}
}
The code works, but I'm wondering if there is a better solution?
To clarify: What I want is adding something like
[
['id' => 25, 'title'=>'some title'],
['id'=> 3, 'title' => 'some other title'],
['id' => 4356, 'title' => 'another title']
]
to my empty database. findOrCreate() does the job. But I think it shouldn't be necessary to test every record that it not already exists in database before inserting.
A common problem with records mysteriously losing some of the data being provided to a new Entity is that the Entity does not define the field(s) in question as _accessible.
Cake's BakeShell will skip the primary key fields when generating new Entity classes for you, for example:
<?php
namespace App\Model\Entity;
use Cake\ORM\Entity;
/**
* Widget Entity.
*/
class Widget extends Entity {
/**
* Fields that can be mass assigned using newEntity() or patchEntity().
*
* #var array
*/
protected $_accessible = [
// `id` is NOT accessible by default!
'title' => true,
];
}
There are a few ways to work around this.
You can modify your Entity class to make the id field permanently assignable:
protected $_accessible = [
'id' => true, // Now `id` can always be mass-assigned.
'title' => true,
];
Or you can adjust your call to newEntity() to disable mass assignment protection:
$entities = $table->newEntity($data, [
'accessibleFields' => ['id' => true],
]);
I've found the most important take-away when you're having issues with Cake 3 DB data is to double-check the Entity as soon as it's created or patched and compare it against your input data. You still need to have a sharp eye, but doing so would reveal that the Entities did not have their ->id property set at all even though $data defined them.
If you really only ever work with empty tables, then you can simply save the data straight away, no need to find and patch, just save with disabled existence check.
Also from looking at your code, the data seems to be in a format that can be turned into entities right away, so you may want to create them all at once.
$entities = $table->newEntities($data, [
// don't forget to restrict assignment one way or
// another when working with external input, for
// example by using the `fieldList` option
'fieldList' => [
'id',
'title'
]
]);
// you may want to check the validation results here before saving
foreach ($entities as $entity) {
if ($table->save($entity, ['checkExisting' => false])) {
// ...
}
// ...
}
See also
Saving Entities
Converting Request Data
Avoiding Property Mass Assignment Attacks
Calidating Data Before Building Entities
I am having a very curious problem. I am trying to do a find with conditions that work across model relationships. To wit...
$this->Model->find('first', array(
'conditions' => array(
'Model.col1' => 'value',
'RelatedModel.col2' => 'value2')));
...assuming that Model has a hasMany relationship to RelatedModel. This particular find bombs out with the following error message:
Warning (512): SQL Error: 1054: Unknown column 'RelatedModel.col2' in 'where clause' [CORE/cake/libs/model/datasources/dbo_source.php, line 525]
Looking at the SELECT being made, I quickly noticed that the comparison in the related model was in fact being placed in the WHERE clause, but for some reason, the only thing in the FROM clause was Model, with no sign of RelatedModel. If I remove the comparison that uses the relationship, related models ARE pulled in the result.
I'm using Cake 1.2.4. At first glance, there's nothing in the 1.2.4 -> 1.2.5 changelog that I see that covers this, and you would think that such an obvious bug would be hunted down and fixed a few days later, as opposed to waiting a full month and not mentioning anything in the release annoucement.
So, uh, what's going on?
If your models are using the Containable behavior, make sure you contain those models.
First, in your {model_name}.php file:
class {ModelName} extends AppModel {
var $actsAs = array('Containable');
}
Then in your find:
$results = $this->Model->find('first', array(
'conditions' => array(
'Model.col1' => 'value',
'RelatedModel.col2' => 'value2',
),
'contain' => array('RelatedModel'),
));
If not using Containable behavior, then try explicitly increasing the recursion level:
$results = $this->Model->find('first', array(
'conditions' => array(
'Model.col1' => 'value',
'RelatedModel.col2' => 'value2',
),
'recursive' => 1,
));
Note that the latter method will more than likely retrieve a lot of unnecessary data, slowing down your application's speed. As such, I highly recommend implementing the use of the Containable behavior.