0

Situation

using Cake 3.2.6

in my CostItemsTable,

I have a buildRules function

/**
 * Returns a rules checker object that will be used for validating
 * application integrity.
 *
 * @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
 * @return \Cake\ORM\RulesChecker
 */
public function buildRules(RulesChecker $rules)
{

    $rules->add($rules->existsIn(['foreign_model_id'], 'ForeignModels'));
    return $rules;
}

What I want

My CostItems Entity has 2 fields called foreign_model and foreign_model_id.

foreign_model_id acts as the foreign key. foreign_model acts as the Table that will be Parent to the CostItems table.

so a typical record can have foreign_model as GeneralCostCategories and foreign_model_id as 1.

What I tried

I tried to log the $this inside the buildRules function but I find nothing useful that allows me to dynamically change this rule.

   $rules->add($rules->existsIn(['foreign_model_id'], 'ForeignModels'));

to

   $rules->add($rules->existsIn(['foreign_model_id'], $entity->foreign_model));
ndm
  • 59,784
  • 9
  • 71
  • 110
Kim Stacks
  • 10,202
  • 35
  • 151
  • 282
  • Note that, by default, when you load a CostItem, the `foreign_model` may be overwritten by the entity referenced by `foreign_model_id`; changing the column name to `foreign_model_name` or some such would eliminate this naming issue. (I think there may also be a configuration you can use to change the property name that will be used for the entity, but I haven't looked into that.) – Greg Schmidt Apr 07 '16 at 15:06

1 Answers1

2

There are various ways to solve this, here's two of them.

* It should be noted that the following is all untested example code!

Custom rules

You could implement a custom rule, either as a callback, or as a rule class, where the entity is going to be passed to, and then run the exists check accordingly with the data from the entity.

callback

use Cake\Datasource\EntityInterface;
use Cake\ORM\Rule\ExistsIn;

// ...

$rules->add(
    function (EntityInterface $entity, array $options) {
        $check = new ExistsIn(['foreign_model_id'], $entity->get('foreign_model'));
        return $check($entity, $options);
    },
    '_existsIn',
    [
        'errorField' => 'foreign_model_id',
        'message' => __d('cake', 'This value does not exist')
    ]
);

custom rule class, src/Model/Rule/MyExitsIn.php

namespace App\Model\Rule;

use Cake\Datasource\EntityInterface;
use Cake\ORM\Rule\ExistsIn;

class MyExistsIn extends ExistsIn
{
    public function __construct($fields)
    {
        parent::__construct($fields, null);
    }

    public function __invoke(EntityInterface $entity, array $options)
    {
        $this->_repository = $entity->get('foreign_model');
        return parent::__invoke($entity, $options);
    }
}
use App\Model\Rule\MyExistsIn;

// ...

$rules->add(
    new MyExistsIn(['foreign_model_id']),
    '_existsIn',
    [
        'errorField' => 'foreign_model_id',
        'message' => __d('cake', 'This value does not exist')
    ]
);

Build rules on the fly

Or use the Model.beforeRules event, which receives the entity too, and modify the rules checker object on the fly.

in your table class

use Cake\Datasource\EntityInterface;
use Cake\Event\Event;

// ...

public function beforeRules(Event $event, EntityInterface $entity, \ArrayObject $options, $operation)
{
    /* @var $rulesChecker \Cake\ORM\RulesChecker */
    $rulesChecker = $this->rulesChecker();
    $rulesChecker->add(
        $rulesChecker->existsIn(['foreign_model_id'], $entity->get('foreign_model'))
    );
}

See also

ndm
  • 59,784
  • 9
  • 71
  • 110