为了简化我的问题,假设我有一个图书馆系统的项目,它实现了我图书馆中图书的借阅、计数、搜索和添加的所有功能。
对于那个项目,我制作了3个主表:person (id, name, etc)
、loan (id, id_person, dates, etc)
,当然还有book (id, title, etc)
。
由于一个人可以同时借多本书,我还需要另一张表来链接这种多对多的关系。rel_loan_book (id, id_book, id_loan)
。
现在,在我的图书视图中,我有一个Gridview
,里面有这本书的所有信息,还有一个列,说明这本书是否可以借阅(目前没有使用)。
以下是我目前正在做的事情:
视图:
[
'attribute' => 'isAvaliable',
'value' => function($model) {
return $model->currentLoan ? 'No' :'Yes';
},
'filter' => Html::activeDropDownList($searchModel, 'isAvaliable', ['1' => 'Yes', '0' => 'No'],
['class'=>'form-control','prompt' => '']
)
],
模型书:
public function getCurrentLoan()
{
return $this->hasOne(Loan::className(), ['id' => 'id_loan'])
->viaTable(RelLoanBook::tableName(), ['id_book' => 'id'])
->onCondition(['loan.status' => 'A']);
}
Loan表中的状态'A'
表示它仍然处于活动状态(该书没有回来)。
我的问题是,当我试图通过可用或不可用的书籍进行搜索时。。。目前,我正在做另一个查询,只是为了检查借来的书是什么,然后在搜索中删除(或过滤)这个id:
图书搜索:
public $isAvaliable;
...
public function rules()
{
return [
[['isAvaliable'], 'safe'],
];
}
...
public function search($params)
{
...
if ($this->isAvaliable !== '') {
$subQuery = Book::find()->select('book.id');
$subQuery->joinWith(['currentLoan'])
->andWhere(['is not' , 'loan.id', null])
->all();
if ($this->isAvaliable === '1') {
$query->andWhere(['not in', 'book.id', $subQuery]);
} elseif ($this->isAvaliable === '0') {
$query->andWhere(['in', 'book.id', $subQuery]);
}
}
...
}
我认为这不是最好的方法。但我无法想象在没有子查询的情况下,在sql查询中进行搜索:
SELECT * FROM book
LEFT JOIN rel_loan_book ON rel_loan_book.book_id = book.id
LEFT JOIN loan ON loan.id = rel_loan_book.loan.id
WHERE ( /* my filters */)
AND ( /* some restriction that check if the book does OR does not have a loan with status === 'A' */)
首先,你有没有理由不把贷款作为结表?这对我来说似乎更合乎逻辑,并且会简化您的查询。
即:
- 个人(id,name)
- 图书(id,姓名,作者,isbn,出版商,流派,标签)
- 贷款(id、person_id、book_id、start_date、due_date、return_date、status)
通过连接账簿和贷款,系统更加灵活,允许:
- 获得的一些头衔的贷款延期
- 某些项目的早期退货
如果你忘了其中一本书,图书馆会拒绝归还所有的书,那就太奇怪了。只有当它们都与一笔贷款相关时,才会发生这种情况。
经过一点结构修改后,将其添加到图书模型中:
public function getIsAvailable()
{
return !Loan::find()->where([
'book_id' => $this->id,
'status' => 'A'
])->exists();
}
如果存在具有活动状态和此账簿id的贷款记录,则该记录不可用。
将isAvailable属性添加到搜索筛选器:
$query->andFilterWhere([
...
'isAvailable' => $this->isAvailable,
]);
将getIsAvailable函数与下拉筛选器链接。
书籍视图中的Adjust isAvailable列:
[
'attribute' => 'isAvailable',
'value' => function($model) {
return $model->isAvailable ? 'Yes' :'No';
},
'filter' => Html::activeDropDownList($searchModel, 'isAvailable',
[0 => 'No', 1 => 'Yes'],
['class'=>'form-control','prompt' => '']
)
],