Skip to content

Instantly share code, notes, and snippets.

@forecho
Last active July 15, 2022 11:14
Show Gist options
  • Save forecho/ed3c7fa3fcb4350a4d512ed91343bad8 to your computer and use it in GitHub Desktop.
Save forecho/ed3c7fa3fcb4350a4d512ed91343bad8 to your computer and use it in GitHub Desktop.
Yii2 Search Model
<?php
/**
* author : forecho <caizhenghai@gmail.com>
* createTime : 2015/12/29 15:33
* description:
*/
namespace common\components;
use yii\base\InvalidParamException;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use yii\data\Pagination;
use yii\db\ActiveQuery;
/**
* // 示例一
*
* ```php
* $searchModel = new SearchModel(
* [
* 'model' => Topic::className(),
* ]
* );
*
* $dataProvider = $searchModel->search(['SearchModel' => Yii::$app->request->queryParams]);
*
* return $this->render('index', [
* 'dataProvider' => $dataProvider,
* ]);
* ```
*
* // 示例二
*
*```php
* $searchModel = new SearchModel(
* [
* 'defaultOrder' => ['id' => SORT_DESC],
* 'model' => Topic::className(),
* 'scenario' => 'default',
* 'relations' => ['comment' => []], // 关联表(可以是Model里面的关联)
* 'partialMatchAttributes' => ['title'], // 模糊查询
* 'pageSize' => 15
* ]
* );
*
* $dataProvider = $searchModel->search(['SearchModel' => Yii::$app->request->queryParams]);
* $dataProvider->query->andWhere([Topic::tableName() . '.user_id' => 23, Comment::tableName() . '.status' => 1]);
*
* return $this->render('index', [
* 'dataProvider' => $dataProvider,
* ]);
* ```
*
* Class SearchModel
* @package common\components
*/
class SearchModel extends Model
{
private $attributes;
private $attributeLabels;
private $internalRelations;
/** @var Model */
private $model;
private $modelClassName;
private $relationAttributes = [];
private $rules;
private $scenarios;
public $defaultOrder;
public $groupBy;
public $pageSize = 20;
public $partialMatchAttributes = []; // 模糊查询
public $relations = [];
/**
* @param ActiveQuery $query
* @param string $attribute
* @param bool $partialMath
*/
private function addCondition($query, $attribute, $partialMath = false)
{
if (isset($this->relationAttributes[$attribute])) {
$attributeName = $this->relationAttributes[$attribute];
} else {
$attributeName = call_user_func([$this->modelClassName, 'tableName']) . '.' . $attribute;
}
$value = $this->$attribute;
if ($value === '') {
return;
}
if ($partialMath) {
$query->andWhere(['like', $attributeName, trim($value)]);
} else {
$query->andWhere($this->conditionTrans($attributeName, $value));
}
}
/**
* 可以查询大于小于和IN、区间查询(><开始时间戳_结束时间戳)
*
* @param $attributeName
* @param $value
* @return array
*/
private function conditionTrans($attributeName, $value)
{
switch (true) {
case is_array($value):
return [$attributeName => $value];
break;
case stripos($value, '>=') !== false:
return ['>=', $attributeName, substr($value, 2)];
break;
case stripos($value, '<=') !== false:
return ['<=', $attributeName, substr($value, 2)];
break;
case stripos($value, '<>') !== false:
return ['<>', $attributeName, substr($value, 2)];
break;
case stripos($value, '><') !== false:
$value = explode('_', $value);
$begin = substr($value[0], 2) ?: time();
$end = $value[1] ?: time();
return ['between', $attributeName, $begin, $end];
break;
case stripos($value, '<') !== false:
return ['<', $attributeName, substr($value, 1)];
break;
case stripos($value, '>') !== false:
return ['>', $attributeName, substr($value, 1)];
break;
case stripos($value, ',') !== false:
return [$attributeName => explode(',', $value)];
break;
default:
return [$attributeName => $value];
break;
}
}
/**
* @param array $params
* @throws \yii\base\InvalidParamException
*/
public function __construct($params)
{
$this->scenario = 'default';
parent::__construct($params);
if ($this->model === null) {
throw new InvalidParamException('Param "model" cannot be empty');
}
$this->rules = $this->model->rules();
$this->scenarios = $this->model->scenarios();
$this->attributeLabels = $this->model->attributeLabels();
foreach ($this->safeAttributes() as $attribute) {
$this->attributes[$attribute] = '';
}
}
/**
* @param string $name
* @return mixed
*/
public function __get($name)
{
if (isset($this->attributes[$name])) {
return $this->attributes[$name];
}
return parent::__get($name);
}
/**
* @param string $name
* @param mixed $value
*/
public function __set($name, $value)
{
if (isset($this->attributes[$name])) {
$this->attributes[$name] = $value;
} else {
parent::__set($name, $value);
}
}
/**
* @return Model
*/
public function getModel()
{
return $this->model;
}
/**
* @param mixed $value
*/
public function setModel($value)
{
if ($value instanceof Model) {
$this->model = $value;
$this->scenario = $this->model->scenario;
$this->modelClassName = get_class($value);
} else {
$this->model = new $value;
$this->modelClassName = $value;
}
}
/**
* @return array
*/
public function rules()
{
return $this->rules;
}
/**
* @return array
*/
public function attributeLabels()
{
return $this->attributeLabels;
}
/**
* @return array
*/
public function scenarios()
{
return $this->scenarios;
}
/**
* @param array $params
* @return ActiveDataProvider
*/
public function search($params)
{
$query = call_user_func([$this->modelClassName, 'find']);
$dataProvider = new ActiveDataProvider(
[
'query' => $query,
'pagination' => new Pagination(
[
'forcePageParam' => false,
'pageSize' => $this->pageSize,
]
),
]
);
if (is_array($this->relations)) {
foreach ($this->relations as $relation => $attributes) {
$pieces = explode('.', $relation);
$path = '';
$parentPath = '';
foreach ($pieces as $i => $piece) {
if ($i == 0) {
$path = $piece;
} else {
$parentPath = $path;
$path .= '.' . $piece;
}
if (!isset($this->internalRelations[$path])) {
if ($i == 0) {
$relationClass = call_user_func([$this->model, 'get' . $piece]);
} else {
$className = $this->internalRelations[$parentPath]['className'];
$relationClass = call_user_func([new $className, 'get' . $piece]);
}
$this->internalRelations[$path] = [
'className' => $relationClass->modelClass,
'tableName' => call_user_func([$relationClass->modelClass, 'tableName']),
];
}
}
foreach ((array)$attributes as $attribute) {
$attributeName = str_replace('.', '_', $relation) . '_' . $attribute;
$tableAttribute = $this->internalRelations[$relation]['tableName'] . '.' . $attribute;
$this->rules[] = [$attributeName, 'safe'];
$this->scenarios[$this->scenario][] = $attributeName;
$this->attributes[$attributeName] = '';
$this->relationAttributes[$attributeName] = $tableAttribute;
$dataProvider->sort->attributes[$attributeName] = [
'asc' => [$tableAttribute => SORT_ASC],
'desc' => [$tableAttribute => SORT_DESC],
];
}
}
$query->joinWith(array_keys($this->relations));
}
if (is_array($this->defaultOrder)) {
$dataProvider->sort->defaultOrder = $this->defaultOrder;
}
if (is_array($this->groupBy)) {
$query->addGroupBy($this->groupBy);
}
$this->load($params);
foreach ($this->attributes as $name => $value) {
$this->addCondition($query, $name, in_array($name, $this->partialMatchAttributes));
}
return $dataProvider;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment