cakephp 3 get property changes entity for dependent model(table) - cakephp-3.0

I want to get the result set with extra field through afterFind. In cakePHP no afterFind function. So used protected function _getAttrName() {
return $this->_properties['name'];
}
inside the Entity class. But i did not get the output with 'attr_name' property. with find()
$this->loadModel('Products');
$this->loadModel('Attributes');
// Get the attributes to use as facets
$attributes = $this->Products->Attributes->find(['all',
'order' => 'Attributes.id',
])
->contain(['AttributeTypes'])
->where([
'Attributes.filterable' => true,
//'Attribute.required' => true,
])
->hydrate(false)
->toArray();
Can i get the solution?

You need to declare in your entity class what are the virtual properties you want to expose when converting to array or to json. This is the relevant section of the docs:
http://book.cakephp.org/3.0/en/orm/entities.html#exposing-virtual-properties
namespace App\Model\Entity;
use Cake\ORM\Entity;
class Product extends Entity
{
protected $_virtual = ['attr_name'];
protected function _getAttrName() {
return $this->_properties['name'];
}
...
}

Related

Yii Query Builder: Parametr binding using where() method

I have this code in my controller:
class ArchController extends Controller
{
public function actionIndex(string $date, array $rubric_id )
{
$articles = Article::find()->where('published < :date', [':date' => $date])
->andWhere(['in', 'rubric', $rubric_id])
->andWhere('ISNULL(arch)')->all();
...
It seems to me it is not safe because $rubric_id is user input. How can I make parametr binding, something like this:
':rubric_id' => $rubric_id
Yii uses parameter binding internally, so it is safe to use ->andWhere(['in', 'rubric', $rubric_id]). You can review implementation of InConditionBuilder to make sure of that.

Integers are marked as dirty attributes no matter what

I need to check if a model has been updated and what attributes have changed when saving.
I'm using dirtyAttributes and filter intval as the docs suggests.
The values are coming from an API and are type-cast as they come in, so in theory the filter is redundant.
Model rules
public function rules()
{
return [
[['contract_date', 'order_date'], 'integer'],
[['contract_date', 'order_date'], 'filter', 'filter' => 'intval'],
];
}
This is some of the code currently running:
// Add the changed status variables to the job log
$dirty_attributes = array_keys($model->dirtyAttributes);
if($model->save()) foreach ($dirty_attributes as $attribute)
{
$data[$attribute] = $model->getOldAttribute($attribute).' ('.gettype($model->getOldAttribute($attribute)).')'. ' => '. $model->$attribute.' ('.gettype($model->$attribute).')';
}
var_dump($data);
This produces:
["contract_date"]=>
string(44) "1559669638 (integer) => 1559669638 (integer)"
["order_date"]=>
string(44) "1559669638 (integer) => 1559669638 (integer)"
There is probably something obvious I'm missing, but I can understand what.
After saving model all "oldAttributes" are updated to store new values so comparing them like you do makes no sense. If you want to check which attributes have been changed after saving you can override afterSave() method in your model like:
public function afterSave($insert, $changedAttributes)
{
// $changedAttributes -> this is it
parent::afterSave(); // call parent to trigger event
}
or listen for ActiveRecord::EVENT_AFTER_INSERT / ActiveRecord::EVENT_AFTER_UPDATE event where this data is also passed.

Sort the parent model based on the child model / relationship

I have a model called appointments, each appointment has an option_id that is linked through a one to one relationship and the option_id can also be null. The option model has a property datetime_start. I want to sort the appointments based on the option.datetime_start.
Here is my code :
$appointments = $user->appointments()
->with(['option'
])
->get();
Edit :
Appointment model :
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Appointment extends Model
{
/**
* #return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function option()
{
return $this->hasOne(Option::class, 'id', 'option_id');
}
}
Option model :
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Option extends Model
{
protected $fillable = ["appointment_id",
"datetime_start"
];
public function appointment()
{
return $this->belongsTo(Appointment::class, 'id','appointment_id');
}
}
Any help would be greatly appreciated.
In order to sort Appointment model by a column from related Option model, you need to add a JOIN to your query so that you can sort by related fields:
$appointments = $user->appointments()
->leftJoin('options', 'options.appointment_id', '=', 'appointments.id')
->orderBy('options.datetime_start', 'ASC')
->with(['option'])
->get();
This will give you all appointments, including those without Option - the ones without an Option and, hence, without datetime_start, will be all returned either at the beginning or the end of the result list, you'll need to check that. If you only want appointments with Option, replace leftJoin() with join().
$appointments = Appointment::with(['option' => function ($query){ $query->orderBy('datetime_start', 'desc'); } ])->get();

Yii2: Non-Scenario fields are not saved

I'm using different scenario for validation purpose.
The field email is not required in a scenario but if i pass email, it is not getting saved in DB.
Rules in Model:
[['firstname','email'], 'string', 'max' => 256],
Scenario function
public function scenarios() {
$scenarios = parent::scenarios();
$scenarios['insert2'] = ['firstname', 'status'];
return $scenarios;
}
In controller:
$model = new User();
$model->scenario = "insert2";
$model->load($data);
print_r($model);
Print_r returns email with empty
From Yii 2 load() docs:
Note, that the data being populated is subject to the safety check by setAttributes().
Now, setAttributes() signature:
public void setAttributes ( $values, $safeOnly = true )
where $safeOnly set to true means the assignments should only be done to the safe attributes. A safe attribute is one that is associated with a validation rule in the current $scenario.
So email must be included in the scenario.
In Yii2, when you want the rule to be applied on certain scenarios, you can specify the on property of a rule, like the following
public function rules()
{
return [
//rule applied only in scenario 'insert2'
[['first_name','status'], 'required','on' => 'insert2'],
//rule applied in all scenarios
['email','safe'],
//rule applied only in scenario 'insert3'
[['first_name','status','email'], 'required','on' => 'insert3'],
];
}
Now if you specify $model->scenario = "insert2" in controller then first_name and status are required and email if you give any value will get saved because ['email','safe'] rule also applied here.
If you specify $model->scenario = "insert3" in controller then first_name,status and email are now required fields and rule ['email','safe'] also get applied.
Please note you may not use public function scenarios() {.. here
From http://www.yiiframework.com/doc-2.0/guide-structure-models.html#validation-rules

How to use constant in the ON condition in Yii2 hasMany relation

I try to create a polymorphic association, what is common in Rails but unfortunately not in Yii2. As part of the implementation I need to define the relation:
public function getImages()
{
return $this->hasMany(RecipeImage::className(),
['imageable_id' => 'id', 'imageable_type' => 'Person']);
}
But this doesn't work, because 'Person' is treated as an attribute of the current model, but it is a constant (class name for the polymorphic association).
If I try to use 'andWhere' it adds the condition of course in a WHERE clause instead of the ON clause, causing that only records with existing image returned.
public function getImages()
{
return $this->hasMany(RecipeImage::className(), ['imageable_id' => 'id'])->
andWhere(['imageable_type' => 'Ingredient']);
}
How can I define the relation? There is no andOn method.
In this case you can modify ON condition with andOnCondition method:
public function getImages()
{
return $this->hasMany(RecipeImage::className(), ['imageable_id' => 'id'])
->andOnCondition(['imageable_type' => 'Person']);
}
Official docs:
andOnCondition: