I would love to be able to extend the Criteria class and do something like this:
User::query
->where('id > 100')
->myCustomMethod()
->orderBy('username')
->execute();
Is this currently possible? Any way at all to do this?
|
Jun '17 |
2 |
509 |
1 |
I would love to be able to extend the Criteria class and do something like this:
User::query
->where('id > 100')
->myCustomMethod()
->orderBy('username')
->execute();
Is this currently possible? Any way at all to do this?
So a little ghetto, but if you override Model::query() you can hijack the the criteria class with your you own implementation:
public static function query(\Phalcon\DiInterface $dependencyInjector = null)
{
/**
* Use the global dependency injector if there is no one defined
*/
if (!is_object($dependencyInjector)) {
$dependencyInjector = \Phalcon\Di::getDefault();
}
$criteria = new \App\Somewhere\Criteria();
$criteria->setDI($dependencyInjector);
$criteria->setModelName(get_called_class());
return $criteria;
}
\App\Somewhere\Criteria
namespace App\Somewhere\Model;
use Phalcon\Mvc\Model\Criteria as PhalconCriteria;
class Criteria extends PhalconCriteria
{
public function myCustomMethod($param)
{
return $this->where("crazy long query you dont want to write every where");
}
}
I don't undesrtand why in the Model class they hardcode the Criteria namespace:
public static function query(<DiInterface> dependencyInjector = null) -> <Criteria>
{
var criteria;
/**
* Use the global dependency injector if there is no one defined
*/
if typeof dependencyInjector != "object" {
let dependencyInjector = Di::getDefault();
}
/**
* Gets Criteria instance from DI container
*/
if dependencyInjector instanceof DiInterface {
// **HERE** WHY IS THIS HARDCODED?
let criteria = <CriteriaInterface> dependencyInjector->get("Phalcon\\Mvc\\Model\\Criteria");
} else {
let criteria = new Criteria();
criteria->setDI(dependencyInjector);
}
criteria->setModelName(get_called_class());
return criteria;
}
https://github.com/phalcon/cphalcon/blob/master/phalcon/mvc/model.zep#L974
Would make more sense to check for a custom implementation before falling back to the Phalcon implementation.
I'd reverse the approach, and create a "criteria manager" class:
class CriteriaManager {
protected $_criteria;
public function attach(CriteriaInterface $criteria) {
$this->_criteria = $criteria;
}
public function myCustomMethod() {
$this->_criteria->andWhere("1=1 AND column='value'");
}
public function execute() {
return $this->_criteria->execute();
}
}
Maybe not with this exact layout, but I'm sure you get the concept ;]
The real pro about this approach is that you don't have to hack into existing classes.
You can also use Phalcon\Db\Dialect\MySQL to create custom function.
Example :
use Phalcon\Db\Dialect\MySQL as SqlDialect;
use Manager\User as UserManager;
$di->set('db', function () use ($config) {
$dialect = new SqlDialect();
$dialect->registerCustomFunction('GET_YEARDIFF', function($dialect, $expression) {
return sprintf(
'TIMESTAMPDIFF(YEAR, %s, %s)',
$dialect->getSqlExpression($expression['arguments'][0]),
$dialect->getSqlExpression($expression['arguments'][1])
);
}
);
$dialect->registerCustomFunction('GROUP_AGE', function($dialect, $expression) {
return str_replace('[age]', $dialect->getSqlExpression($expression['arguments'][0]),
'CASE
WHEN [age] <= 17 THEN \''.UserManager::$ageGroup[0]['name'].'\'
WHEN [age] <= 25 THEN \''.UserManager::$ageGroup[1]['name'].'\'
WHEN [age] <= 35 THEN \''.UserManager::$ageGroup[2]['name'].'\'
WHEN [age] <= 50 THEN \''.UserManager::$ageGroup[3]['name'].'\'
WHEN [age] > 50 THEN \''.UserManager::$ageGroup[4]['name'].'\'
ELSE \''.UserManager::$ageGroup[5]['name'].'\'
END'
);
}
);
return new Mysql([
'host' => $config[ENV]->database->host,
'username' => $config[ENV]->database->username,
'password' => $config[ENV]->database->password,
'dbname' => $config[ENV]->database->dbname,
'charset' => $config[ENV]->database->charset,
'dialectClass' => $dialect
]);
});
User::find(['columns'=>'*, GROUP_AGE(GET_YEARDIFF(us_birthday_date, current_date())) as groupAgeName']);
Thats probably a better idea. How would you then hook this into your model? I still want to be able to call Model::query() so that I don't break the existing api.
I'd reverse the approach, and create a "criteria manager" class:
class CriteriaManager { protected $_criteria; public function attach(CriteriaInterface $criteria) { $this->_criteria = $criteria; } public function myCustomMethod() { $this->_criteria->andWhere("1=1 AND column='value'"); } public function execute() { return $this->_criteria->execute(); } }
Maybe not with this exact layout, but I'm sure you get the concept ;]
The real pro about this approach is that you don't have to hack into existing classes.
Yes, dialects are also part of what I want to do. Wouldnt it be great to do this?
$ageGroups = User::query()->groupByAge()->execute();
Code doesnt come much cleaner than that.
You can also use Phalcon\Db\Dialect\MySQL to create custom function.
Example :use Phalcon\Db\Dialect\MySQL as SqlDialect; use Manager\User as UserManager; $di->set('db', function () use ($config) { $dialect = new SqlDialect(); $dialect->registerCustomFunction('GET_YEARDIFF', function($dialect, $expression) { return sprintf( 'TIMESTAMPDIFF(YEAR, %s, %s)', $dialect->getSqlExpression($expression['arguments'][0]), $dialect->getSqlExpression($expression['arguments'][1]) ); } ); $dialect->registerCustomFunction('GROUP_AGE', function($dialect, $expression) { return str_replace('[age]', $dialect->getSqlExpression($expression['arguments'][0]), 'CASE WHEN [age] <= 17 THEN \''.UserManager::$ageGroup[0]['name'].'\' WHEN [age] <= 25 THEN \''.UserManager::$ageGroup[1]['name'].'\' WHEN [age] <= 35 THEN \''.UserManager::$ageGroup[2]['name'].'\' WHEN [age] <= 50 THEN \''.UserManager::$ageGroup[3]['name'].'\' WHEN [age] > 50 THEN \''.UserManager::$ageGroup[4]['name'].'\' ELSE \''.UserManager::$ageGroup[5]['name'].'\' END' ); } ); return new Mysql([ 'host' => $config[ENV]->database->host, 'username' => $config[ENV]->database->username, 'password' => $config[ENV]->database->password, 'dbname' => $config[ENV]->database->dbname, 'charset' => $config[ENV]->database->charset, 'dialectClass' => $dialect ]); }); User::find(['columns'=>'*, GROUP_AGE(GET_YEARDIFF(us_birthday_date, current_date())) as groupAgeName']);