我想为 cakephp3 应用程序编写一个简单的标记插件。假设我们有一个模型 books 和一个模型 reviews。对于每个模型,应该可以附加标签 - 只需添加一个行为(在插件中):
$this->addBehavior('Tag.Taggable')
。
我在数据库中创建了两个表:
tags, tagged_tags
。
表 tagged_tags:
id | tag_id | tagged_id |
1 | 1 | 1 |
2 | 2 | 1 |
tagged_id 是标记实体的 id。它属于哪个型号的信息在另一个表中。 表格标签:
id | tag | model |
1 | book | App\Model\Table\BooksTable |
2 | nobook | App\Model\Table\ReviewsTable|
显然,只有第一个标签属于一本书。
class TaggableBehavior extends Behavior
{
// Some more code here
public function __construct(Table $table, array $config = [])
{
parent::__construct($table, $config);
$this->_table = $table;
$this->_table->belongsToMany('Tag.Tags', [
'joinTable' => 'tagged_tags',
'foreignKey' => 'tagged_id',
'targetForeignKey' => 'tag_id',
'conditions' => [
'Tags.model' => get_class($this->_table);
]
]);
}
}
检索数据工作完美。但节省是一个问题:
错误:SQLSTATE[42S22]:未找到列:1054 未知列 “where 子句”中的“Tags.model”
SQL查询:
选择 TaggedTags.id AS
,TaggedTags.tagged_id ASTaggedTags__id
,TaggedTags.tag_id ASTaggedTags__tagged_id
来自 tagged_tags TaggedTags WHERE (tagged_id = :c0 AND Tags.model = :c1)TaggedTags__tag_id
我不太确定为什么 cakephp 在这里执行 SELECT 查询,我也不在乎。为什么这个查询会导致错误是很清楚的。但我的错误在哪里呢?这与
'conditions' => ['Tags.model' => get_class($this->_table);
有关。没有这个,我可以保存数据(但不能说哪个标签属于一本书或不属于一本书)
编辑:一些附加信息
这里是完整的sql语句,显示在调试工具包中http://freetexthost.com/tc3s46nugi
控制器代码:
public function add()
{
$book = $this->Books->newEntity();
if ($this->request->is('post')) {
$book = $this->Books->patchEntity($book, $this->request->data);
if ($this->Books->save($book)) {
$this->Flash->success(__('The book has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The book could not be saved. Please, try again.'));
}
}
在行为中,我有一些来自书签教程的逻辑(复制/粘贴)
public function beforeSave($event, $entity, $options)
{
if ($entity->tag_string) {
$entity->tags = $this->_buildTags($entity->tag_string);
}
}
protected function _buildTags($tagString)
{
$new = array_unique(array_map('trim', explode(',', $tagString)));
$out = [];
$query = $this->_table->Tags->find()
->where([
'Tags.tag IN' => $new,
'Tags.model' => $this->name()
]);
// Remove existing tags from the list of new tags.
foreach ($query->extract('tag') as $existing) {
$index = array_search($existing, $new);
if ($index !== false) {
unset($new[$index]);
}
}
// Add existing tags.
foreach ($query as $tag) {
$tag['count'] = $tag['count']+1;
$out[] = $tag;
}
// Add new tags.
foreach ($new as $tag) {
$out[] = $this->_table->Tags->newEntity(['tag' => $tag, 'model' => $this->name(), 'count' => 0]);
}
return $out;
}
如果您创建标签模型,并创建与其他模型的 HABTM 关系,您将制作简单的标记插件。
这里有简单的指南。
添加.ctp
省略多个选择标签字段,而是放置标签文本字段(例如tagsinput)。添加一些 jquery 标记插件。添加新标签(关键字)时,它会立即通过 jquery post 方法存储在标签表中。如果标签表中存在该关键字,则不要再次存储。
行为
在 beforeSave 方法中处理来自标记字段的值。 如果值用逗号分隔,您可以使用类似这样的内容 (CakePHP 2):
public function beforeSave($options = array())
{
if(!empty($this->data['Article']['tagsinput'])) {
$tags = explode(',', $this->data['Article']['tagsinput']);
foreach ($tags as $tag) {
$tagKey = $this->Tag->find('first',array(
'recursive' => -1,
'fields' => array('id'),
'conditions' => array('Tag.name' => $tag)
));
$this->data['Tag']['Tag'][] = $tagKey['Tag']['id'];
}
}
return true;
}
这将创建 HABTM 关系并将这些值存储在表articles_tags中。
编辑.ctp
创建逗号分隔值:
<?php
$extract_tags = Hash::extract($this->data['Tag'],'{n}.name');
$tags = implode(',', $extract_tags);
echo $this->Form->input('tagsinput',
array(
'id' => 'tags',
'div'=>true,
'label'=>__('Tags'),
'class'=>'form-control input-lg',
'placeholder'=>__('Add keywords here'),
'value' => $tags
)
);
?>