Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Split rules up into ORM and non-ORM flavours.
Having a RulesChecker independent of the ORM will be very helpful when implementing rules on the elastic search ODM. This should be fully backwards compatible as the old classes and methods are still around.
- Loading branch information
Showing
4 changed files
with
408 additions
and
347 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
<?php | ||
namespace Cake\Datasource; | ||
|
||
use ArrayObject; | ||
use Cake\Datasource\EntityInterface; | ||
use Cake\Datasource\RulesChecker; | ||
|
||
/** | ||
* A trait that allows a class to build and apply application. | ||
* rules. | ||
* | ||
* If the implementing class also implements EventAwareTrait, then | ||
* events will be emitted when rules are checked. | ||
* | ||
* The implementing class is expected to define the `RULES_CLASS` constant | ||
* if they need to customize which class is used for rules objects. | ||
*/ | ||
trait RulesAwareTrait | ||
{ | ||
/** | ||
* The domain rules to be applied to entities saved by this table | ||
* | ||
* @var \Cake\Datasource\RulesChecker | ||
*/ | ||
protected $_rulesChecker; | ||
|
||
/** | ||
* Returns whether or not the passed entity complies with all the rules stored in | ||
* the rules checker. | ||
* | ||
* @param \Cake\Datasource\EntityInterface $entity The entity to check for validity. | ||
* @param string $operation The operation being run. Either 'create', 'update' or 'delete'. | ||
* @param \ArrayObject|array $options The options To be passed to the rules. | ||
* @return bool | ||
*/ | ||
public function checkRules(EntityInterface $entity, $operation = RulesChecker::CREATE, $options = null) | ||
{ | ||
$rules = $this->rulesChecker(); | ||
$options = $options ?: new ArrayObject; | ||
$options = is_array($options) ? new ArrayObject($options) : $options; | ||
$hasEvents = method_exists($this, 'dispatchEvent'); | ||
|
||
if ($hasEvents) { | ||
$event = $this->dispatchEvent( | ||
'Model.beforeRules', | ||
compact('entity', 'options', 'operation') | ||
); | ||
if ($event->isStopped()) { | ||
return $event->result; | ||
} | ||
} | ||
|
||
$result = $rules->check($entity, $operation, $options->getArrayCopy()); | ||
|
||
if ($hasEvents) { | ||
$event = $this->dispatchEvent( | ||
'Model.afterRules', | ||
compact('entity', 'options', 'result', 'operation') | ||
); | ||
|
||
if ($event->isStopped()) { | ||
return $event->result; | ||
} | ||
} | ||
return $result; | ||
} | ||
|
||
/** | ||
* Returns the rule checker for this table. A rules checker object is used to | ||
* test an entity for validity on rules that may involve complex logic or data that | ||
* needs to be fetched from the database or other sources. | ||
* | ||
* @return \Cake\Datasource\RulesChecker | ||
*/ | ||
public function rulesChecker() | ||
{ | ||
if ($this->_rulesChecker !== null) { | ||
return $this->_rulesChecker; | ||
} | ||
$class = defined('static::RULES_CLASS') ? static::RULES_CLASS : 'Cake\Datasource\RulesChecker'; | ||
$this->_rulesChecker = $this->buildRules(new $class(['repository' => $this])); | ||
$this->dispatchEvent('Model.buildRules', ['rules' => $this->_rulesChecker]); | ||
return $this->_rulesChecker; | ||
} | ||
|
||
/** | ||
* Returns rules checker object after modifying the one that was passed. Subclasses | ||
* can override this method in order to initialize the rules to be applied to | ||
* entities saved by this table. | ||
* | ||
* @param \Cake\Datasource\RulesChecker $rules The rules object to be modified. | ||
* @return \Cake\Datasource\RulesChecker | ||
*/ | ||
public function buildRules(RulesChecker $rules) | ||
{ | ||
return $rules; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,296 @@ | ||
<?php | ||
/** | ||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org) | ||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* | ||
* Licensed under The MIT License | ||
* For full copyright and license information, please see the LICENSE.txt | ||
* Redistributions of files must retain the above copyright notice. | ||
* | ||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* @link http://cakephp.org CakePHP(tm) Project | ||
* @since 3.0.7 | ||
* @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
*/ | ||
namespace Cake\Datasource; | ||
|
||
use ArrayObject; | ||
use BadMethodCallException; | ||
use InvalidArgumentException; | ||
|
||
/** | ||
* Contains logic for storing and checking rules on entities | ||
* | ||
* RulesCheckers are used by Table classes to ensure that the | ||
* current entity state satisfies the application logic and business rules. | ||
* | ||
* RulesCheckers afford different rules to be applied in the create and update | ||
* scenario. | ||
* | ||
* ### Adding rules | ||
* | ||
* Rules must be callable objects that return true/false depending on whether or | ||
* not the rule has been satisfied. You can use RulesChecker::add(), RulesChecker::addCreate(), | ||
* RulesChecker::addUpdate() and RulesChecker::addDelete to add rules to a checker. | ||
* | ||
* ### Running checks | ||
* | ||
* Generally a Table object will invoke the rules objects, but you can manually | ||
* invoke the checks by calling RulesChecker::checkCreate(), RulesChecker::checkUpdate() or | ||
* RulesChecker::checkDelete(). | ||
*/ | ||
class RulesChecker | ||
{ | ||
/** | ||
* Indicates that the checking rules to apply are those used for creating entities | ||
* | ||
* @var string | ||
*/ | ||
const CREATE = 'create'; | ||
|
||
/** | ||
* Indicates that the checking rules to apply are those used for updating entities | ||
* | ||
* @var string | ||
*/ | ||
const UPDATE = 'update'; | ||
|
||
/** | ||
* Indicates that the checking rules to apply are those used for deleting entities | ||
* | ||
* @var string | ||
*/ | ||
const DELETE = 'delete'; | ||
|
||
/** | ||
* The list of rules to be checked on both create and update operations | ||
* | ||
* @var array | ||
*/ | ||
protected $_rules = []; | ||
|
||
/** | ||
* The list of rules to check during create operations | ||
* | ||
* @var array | ||
*/ | ||
protected $_createRules = []; | ||
|
||
/** | ||
* The list of rules to check during update operations | ||
* | ||
* @var array | ||
*/ | ||
protected $_updateRules = []; | ||
|
||
/** | ||
* The list of rules to check during delete operations | ||
* | ||
* @var array | ||
*/ | ||
protected $_deleteRules = []; | ||
|
||
/** | ||
* List of options to pass to every callable rule | ||
* | ||
* @var array | ||
*/ | ||
protected $_options = []; | ||
|
||
/** | ||
* Whether or not to use I18n functions for translating default error messages | ||
* | ||
* @var bool | ||
*/ | ||
protected $_useI18n = false; | ||
|
||
/** | ||
* Constructor. Takes the options to be passed to all rules. | ||
* | ||
* @param array $options The options to pass to every rule | ||
*/ | ||
public function __construct(array $options) | ||
{ | ||
$this->_options = $options; | ||
$this->_useI18n = function_exists('__d'); | ||
} | ||
|
||
/** | ||
* Adds a rule that will be applied to the entity both on create and update | ||
* operations. | ||
* | ||
* ### Options | ||
* | ||
* The options array accept the following special keys: | ||
* | ||
* - `errorField`: The name of the entity field that will be marked as invalid | ||
* if the rule does not pass. | ||
* - `message`: The error message to set to `errorField` if the rule does not pass. | ||
* | ||
* @param callable $rule A callable function or object that will return whether | ||
* the entity is valid or not. | ||
* @param string $name The alias for a rule. | ||
* @param array $options List of extra options to pass to the rule callable as | ||
* second argument. | ||
* @return $this | ||
*/ | ||
public function add(callable $rule, $name = null, array $options = []) | ||
{ | ||
$this->_rules[] = $this->_addError($rule, $name, $options); | ||
return $this; | ||
} | ||
|
||
/** | ||
* Adds a rule that will be applied to the entity on create operations. | ||
* | ||
* ### Options | ||
* | ||
* The options array accept the following special keys: | ||
* | ||
* - `errorField`: The name of the entity field that will be marked as invalid | ||
* if the rule does not pass. | ||
* - `message`: The error message to set to `errorField` if the rule does not pass. | ||
* | ||
* @param callable $rule A callable function or object that will return whether | ||
* the entity is valid or not. | ||
* @param string $name The alias for a rule. | ||
* @param array $options List of extra options to pass to the rule callable as | ||
* second argument. | ||
* @return $this | ||
*/ | ||
public function addCreate(callable $rule, $name = null, array $options = []) | ||
{ | ||
$this->_createRules[] = $this->_addError($rule, $name, $options); | ||
return $this; | ||
} | ||
|
||
/** | ||
* Adds a rule that will be applied to the entity on update operations. | ||
* | ||
* ### Options | ||
* | ||
* The options array accept the following special keys: | ||
* | ||
* - `errorField`: The name of the entity field that will be marked as invalid | ||
* if the rule does not pass. | ||
* - `message`: The error message to set to `errorField` if the rule does not pass. | ||
* | ||
* @param callable $rule A callable function or object that will return whether | ||
* the entity is valid or not. | ||
* @param string $name The alias for a rule. | ||
* @param array $options List of extra options to pass to the rule callable as | ||
* second argument. | ||
* @return $this | ||
*/ | ||
public function addUpdate(callable $rule, $name = null, array $options = []) | ||
{ | ||
$this->_updateRules[] = $this->_addError($rule, $name, $options); | ||
return $this; | ||
} | ||
|
||
/** | ||
* Adds a rule that will be applied to the entity on delete operations. | ||
* | ||
* ### Options | ||
* | ||
* The options array accept the following special keys: | ||
* | ||
* - `errorField`: The name of the entity field that will be marked as invalid | ||
* if the rule does not pass. | ||
* - `message`: The error message to set to `errorField` if the rule does not pass. | ||
* | ||
* @param callable $rule A callable function or object that will return whether | ||
* the entity is valid or not. | ||
* @param string $name The alias for a rule. | ||
* @param array $options List of extra options to pass to the rule callable as | ||
* second argument. | ||
* @return $this | ||
*/ | ||
public function addDelete(callable $rule, $name = null, array $options = []) | ||
{ | ||
$this->_deleteRules[] = $this->_addError($rule, $name, $options); | ||
return $this; | ||
} | ||
|
||
/** | ||
* Runs each of the rules by passing the provided entity and returns true if all | ||
* of them pass. The rules to be applied are depended on the $mode parameter which | ||
* can only be RulesChecker::CREATE, RulesChecker::UPDATE or RulesChecker::DELETE | ||
* | ||
* @param \Cake\Datasource\EntityInterface $entity The entity to check for validity. | ||
* @param string $mode Either 'create, 'update' or 'delete'. | ||
* @param array $options Extra options to pass to checker functions. | ||
* @return bool | ||
* @throws \InvalidArgumentException if an invalid mode is passed. | ||
*/ | ||
public function check(EntityInterface $entity, $mode, array $options = []) | ||
{ | ||
if ($mode === self::CREATE) { | ||
return $this->checkCreate($entity, $options); | ||
} | ||
|
||
if ($mode === self::UPDATE) { | ||
return $this->checkUpdate($entity, $options); | ||
} | ||
|
||
if ($mode === self::DELETE) { | ||
return $this->checkDelete($entity, $options); | ||
} | ||
|
||
throw new InvalidArgumentException('Wrong checking mode: ' . $mode); | ||
} | ||
|
||
/** | ||
* Runs each of the rules by passing the provided entity and returns true if all | ||
* of them pass. The rules selected will be only those specified to be run on 'create' | ||
* | ||
* @param \Cake\Datasource\EntityInterface $entity The entity to check for validity. | ||
* @param array $options Extra options to pass to checker functions. | ||
* @return bool | ||
*/ | ||
public function checkCreate(EntityInterface $entity, array $options = []) | ||
{ | ||
$success = true; | ||
$options = $options + $this->_options; | ||
foreach (array_merge($this->_rules, $this->_createRules) as $rule) { | ||
$success = $rule($entity, $options) && $success; | ||
} | ||
return $success; | ||
} | ||
|
||
/** | ||
* Runs each of the rules by passing the provided entity and returns true if all | ||
* of them pass. The rules selected will be only those specified to be run on 'update' | ||
* | ||
* @param \Cake\Datasource\EntityInterface $entity The entity to check for validity. | ||
* @param array $options Extra options to pass to checker functions. | ||
* @return bool | ||
*/ | ||
public function checkUpdate(EntityInterface $entity, array $options = []) | ||
{ | ||
$success = true; | ||
$options = $options + $this->_options; | ||
foreach (array_merge($this->_rules, $this->_updateRules) as $rule) { | ||
$success = $rule($entity, $options) && $success; | ||
} | ||
return $success; | ||
} | ||
|
||
/** | ||
* Runs each of the rules by passing the provided entity and returns true if all | ||
* of them pass. The rules selected will be only those specified to be run on 'delete' | ||
* | ||
* @param \Cake\Datasource\EntityInterface $entity The entity to check for validity. | ||
* @param array $options Extra options to pass to checker functions. | ||
* @return bool | ||
*/ | ||
public function checkDelete(EntityInterface $entity, array $options = []) | ||
{ | ||
$success = true; | ||
$options = $options + $this->_options; | ||
foreach ($this->_deleteRules as $rule) { | ||
$success = $rule($entity, $options) && $success; | ||
} | ||
return $success; | ||
} | ||
} |
Oops, something went wrong.
19c0c7c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@markstory This update isn't quite as BC for some: "Declaration of App\Model\Table\AdsTable::buildRules() should be compatible with Cake\ORM\Table::buildRules(Cake\Datasource\RulesChecker $rules)"
Since it was moved to a different class people will have to adjust their use statements accordingly. Should be communicate that in a release note?
19c0c7c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for exemple : I've the error with this code.
public function buildRules(RulesChecker $rules) {
$rules->add($rules->existsIn(['ads_category_id'], 'AdsCategories'));
$rules->add($rules->existsIn(['ads_type_id'], 'AdsTypes'));
$rules->add($rules->existsIn(['country_id'], 'Countries'));
$rules->add($rules->existsIn(['region_id'], 'Regions'));
$rules->add($rules->existsIn(['departement_id'], 'Departements'));
return $rules;
}
19c0c7c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is going to generate error in every table that has
buildRules()
method.19c0c7c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we don't need buildrules() method ?
19c0c7c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No you do, I'll fix it up so the code is actually backwards compatible.
19c0c7c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if I left :
public function buildRules(RulesChecker $rules) {
}
I ve de same error after a composer update.
19c0c7c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@LeFuret17 Yes, right now master is not backwards compatible. I will have it fixed today.
19c0c7c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so now we dont use this?
When i make a bake model it's wrong too ?
19c0c7c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@LeFuret17 The code in master is broken. The code you pasted should work, but doesn't right now because of the defect.
19c0c7c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So i diseabled all buildrules in Models and i wait for the correction
19c0c7c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@LeFuret17 It is fixed now.
19c0c7c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok for me it's good