src/models/forms/SearchForm.php
<?php
namespace bizley\podium\models\forms;
use bizley\podium\models\Category;
use bizley\podium\models\Forum;
use bizley\podium\models\Post;
use bizley\podium\models\Thread;
use bizley\podium\models\Vocabulary;
use bizley\podium\Podium;
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use yii\db\ActiveQuery;
use yii\helpers\HtmlPurifier;
/**
* SearchForm model
* Advanced forum search.
*
* @author Paweł Bizley Brzozowski <pawel@positive.codes>
* @since 0.6
*/
class SearchForm extends Model
{
/**
* @var string Query
*/
public $query;
/**
* @var string Whether to search for all words or any word
*/
public $match;
/**
* @var string Author name
*/
public $author;
/**
* @var string Date from [yyyy-MM-dd]
*/
public $dateFrom;
/**
* @var int Date from stamp
* @ince 0.6
*/
public $dateFromStamp;
/**
* @var string Date to [yyyy-MM-dd]
*/
public $dateTo;
/**
* @var int Date to stamp
* @since 0.6
*/
public $dateToStamp;
/**
* @var string Whether to search for posts or topics
*/
public $forums;
/**
* @var string Whether to search for posts or topics
*/
public $type;
/**
* @var string Whether to display results as posts or topics
*/
public $display;
/**
* @var bool Whether the next search page is being loaded
* @since 0.8
*/
public $nextPage = false;
/**
* @inheritdoc
*/
public function rules()
{
return [
[['query', 'author'], 'string'],
[['query', 'author'], 'filter', 'filter' => function ($value) {
return HtmlPurifier::process(trim($value));
}],
['query', 'string', 'min' => 3],
['author', 'string', 'min' => 2],
[['match'], 'in', 'range' => ['all', 'any']],
[['dateFrom', 'dateTo'], 'default', 'value' => null],
['dateFrom', 'date', 'format' => 'yyyy-MM-dd', 'timestampAttribute' => 'dateFromStamp'],
['dateTo', 'date', 'format' => 'yyyy-MM-dd', 'timestampAttribute' => 'dateToStamp'],
[['dateFromStamp', 'dateToStamp'], 'integer'],
[['forums'], 'each', 'rule' => ['integer']],
[['type', 'display'], 'in', 'range' => ['posts', 'topics']],
];
}
/**
* Sets search parameters based on the session.
* @since 0.8
*/
public function setParams()
{
if (Yii::$app->request->get('page') && Yii::$app->session->has('forum-search')) {
$session = Yii::$app->session->get('forum-search');
$this->query = $session['query'];
$this->match = $session['match'];
$this->author = $session['author'];
$this->dateFrom = $session['dateFrom'];
$this->dateFromStamp = $session['dateFromStamp'];
$this->dateTo = $session['dateTo'];
$this->dateToStamp = $session['dateToStamp'];
$this->forums = $session['forums'];
$this->type = $session['type'];
$this->display = $session['display'];
$this->nextPage = true;
} else {
$this->match = 'all';
$this->type = 'posts';
$this->display = 'topics';
$this->nextPage = false;
}
}
/**
* Prepares query conditions.
* @param ActiveQuery $query
* @param bool $topics
* @since 0.6
*/
protected function prepareQuery($query, $topics = false)
{
$field = $topics
? Thread::tableName() . '.created_at'
: Post::tableName() . '.updated_at';
if (!empty($this->author)) {
$query->andWhere(['like', 'username', $this->author])->joinWith(['author']);
}
if (!empty($this->dateFromStamp) && empty($this->dateToStamp)) {
$query->andWhere(['>=', $field, $this->dateFromStamp]);
} elseif (!empty($this->dateToStamp) && empty($this->dateFromStamp)) {
$this->dateToStamp += 23 * 3600 + 59 * 60 + 59; // 23:59:59
$query->andWhere(['<=', $field, $this->dateToStamp]);
} elseif (!empty($this->dateToStamp) && !empty($this->dateFromStamp)) {
if ($this->dateFromStamp > $this->dateToStamp) {
$tmp = $this->dateToStamp;
$this->dateToStamp = $this->dateFromStamp;
$this->dateFromStamp = $tmp;
}
$this->dateToStamp += 23 * 3600 + 59 * 60 + 59; // 23:59:59
$query->andWhere(['<=', $field, $this->dateToStamp]);
$query->andWhere(['>=', $field, $this->dateFromStamp]);
}
if (!empty($this->forums)) {
if (is_array($this->forums)) {
$forums = [];
foreach ($this->forums as $f) {
if (is_numeric($f)) {
$forums[] = (int)$f;
}
}
if (!empty($forums)) {
$query->andWhere(['forum_id' => $forums]);
}
}
}
}
/**
* Advanced topics search.
* @return ActiveDataProvider
* @since 0.6
*/
protected function searchTopics()
{
$query = Thread::find();
if (Podium::getInstance()->user->isGuest) {
$query->joinWith(['forum' => function ($q) {
$q->andWhere([Forum::tableName() . '.visible' => 1])->joinWith(['category' => function ($q) {
$q->andWhere([Category::tableName() . '.visible' => 1]);
}]);
}]);
}
if (!empty($this->query)) {
$words = explode(' ', preg_replace('/\s+/', ' ', $this->query));
foreach ($words as $word) {
if ($this->match == 'all') {
$query->andWhere(['like', Thread::tableName() . '.name', $word]);
} else {
$query->orWhere(['like', Thread::tableName() . '.name', $word]);
}
}
}
$this->prepareQuery($query, true);
$sort = [
'defaultOrder' => [Thread::tableName() . '.id' => SORT_DESC],
'attributes' => [
Thread::tableName() . '.id' => [
'asc' => [Thread::tableName() . '.id' => SORT_ASC],
'desc' => [Thread::tableName() . '.id' => SORT_DESC],
'default' => SORT_DESC,
],
]
];
return new ActiveDataProvider([
'query' => $query,
'sort' => $sort,
]);
}
/**
* Advanced posts search.
* @return ActiveDataProvider
* @since 0.6
*/
public function searchPosts()
{
$query = Vocabulary::find()->select('post_id, thread_id')->joinWith(['posts.author', 'posts.thread'])->andWhere(['is not', 'post_id', null]);
if (Podium::getInstance()->user->isGuest) {
$query->joinWith(['posts.forum' => function ($q) {
$q->andWhere([Forum::tableName() . '.visible' => 1])->joinWith(['category' => function ($q) {
$q->andWhere([Category::tableName() . '.visible' => 1]);
}]);
}]);
}
if (!empty($this->query)) {
$words = explode(' ', preg_replace('/\s+/', ' ', $this->query));
$countWords = 0;
foreach ($words as $word) {
$query->orWhere(['like', 'word', $word]);
$countWords++;
}
$query->groupBy('post_id');
if ($this->match == 'all' && $countWords > 1) {
$query->select(['post_id', 'thread_id', 'COUNT(post_id) AS c'])->having(['>', 'c', $countWords - 1]);
}
}
$this->prepareQuery($query);
$sort = [
'defaultOrder' => ['post_id' => SORT_DESC],
'attributes' => [
'post_id' => [
'asc' => ['post_id' => SORT_ASC],
'desc' => ['post_id' => SORT_DESC],
'default' => SORT_DESC,
],
]
];
return new ActiveDataProvider([
'query' => $query,
'sort' => $sort,
]);
}
/**
* Advanced search.
* @return ActiveDataProvider
*/
public function searchAdvanced()
{
if (!$this->nextPage) {
Yii::$app->session->set('forum-search', [
'query' => $this->query,
'match' => $this->match,
'author' => $this->author,
'dateFrom' => $this->dateFrom,
'dateFromStamp' => $this->dateFromStamp,
'dateTo' => $this->dateTo,
'dateToStamp' => $this->dateToStamp,
'forums' => $this->forums,
'type' => $this->type,
'display' => $this->display
]);
}
if ($this->type == 'topics') {
return $this->searchTopics();
}
return $this->searchPosts();
}
}