Skip to content

Commit 302cfb6

Browse files
committed
Get tag list creation and saving fixed
Refs cakephp/cakephp#17667
1 parent 3610494 commit 302cfb6

3 files changed

Lines changed: 91 additions & 8 deletions

File tree

src/Controller/ArticlesController.php

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,12 @@ public function delete($slug)
8585
}
8686
}
8787

88-
public function tags()
88+
public function tags(array $tags = [])
8989
{
9090
$this->Authorization->skipAuthorization();
9191

92-
// The 'pass' key is provided by CakePHP and contains all
93-
// the passed URL path segments in the request.
94-
$tags = $this->request->getParam('pass');
95-
9692
// Use the ArticlesTable to find tagged articles.
97-
$articles = $this->Articles->find('tagged', [
98-
'tags' => $tags
99-
]);
93+
$articles = $this->Articles->find('tagged', tags: $tags);
10094

10195
// Pass variables into the view template context.
10296
$this->set([

src/Model/Entity/Article.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
namespace App\Model\Entity;
55

6+
use Cake\Collection\Collection;
67
use Cake\ORM\Entity;
78
use Cake\Utility\Text;
89

@@ -42,6 +43,7 @@ class Article extends Entity
4243
'modified' => true,
4344
'user' => true,
4445
'tags' => true,
46+
'tag_string' => true,
4547
];
4648

4749
protected function _setTitle(string $title): string
@@ -50,4 +52,20 @@ protected function _setTitle(string $title): string
5052

5153
return $title;
5254
}
55+
56+
protected function _getTagString(): string
57+
{
58+
if (isset($this->_fields['tag_string'])) {
59+
return $this->_fields['tag_string'];
60+
}
61+
if (empty($this->tags)) {
62+
return '';
63+
}
64+
$tags = new Collection($this->tags);
65+
$str = $tags->reduce(function ($string, $tag) {
66+
return $string . $tag->title . ', ';
67+
}, '');
68+
69+
return trim($str, ', ');
70+
}
5371
}

src/Model/Table/ArticlesTable.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
namespace App\Model\Table;
55

6+
use ArrayObject;
7+
use Cake\Event\EventInterface;
8+
use Cake\ORM\Query\SelectQuery;
69
use Cake\ORM\RulesChecker;
710
use Cake\ORM\Table;
811
use Cake\Validation\Validator;
@@ -106,4 +109,72 @@ public function buildRules(RulesChecker $rules): RulesChecker
106109

107110
return $rules;
108111
}
112+
113+
public function beforeSave(EventInterface $event, $entity, ArrayObject $options)
114+
{
115+
if ($entity->tag_string) {
116+
$entity->tags = $this->_buildTags($entity->tag_string);
117+
}
118+
119+
// Other code
120+
}
121+
122+
protected function _buildTags($tagString)
123+
{
124+
// Trim tags
125+
$newTags = array_map('trim', explode(',', $tagString));
126+
// Remove all empty tags
127+
$newTags = array_filter($newTags);
128+
// Reduce duplicated tags
129+
$newTags = array_unique($newTags);
130+
131+
$out = [];
132+
$tags = $this->Tags->find()
133+
->where(['Tags.title IN' => $newTags])
134+
->all();
135+
136+
// Remove existing tags from the list of new tags.
137+
foreach ($tags->extract('title') as $existing) {
138+
$index = array_search($existing, $newTags);
139+
if ($index !== false) {
140+
unset($newTags[$index]);
141+
}
142+
}
143+
// Add existing tags.
144+
foreach ($tags as $tag) {
145+
$out[] = $tag;
146+
}
147+
// Add new tags.
148+
foreach ($newTags as $tag) {
149+
$out[] = $this->Tags->newEntity(['title' => $tag]);
150+
}
151+
return $out;
152+
}
153+
154+
// The $query argument is a query builder instance.
155+
// The $options array will contain the 'tags' option we passed
156+
// to find('tagged') in our controller action.
157+
public function findTagged(SelectQuery $query, array $tags = []): SelectQuery
158+
{
159+
$columns = [
160+
'Articles.id', 'Articles.user_id', 'Articles.title',
161+
'Articles.body', 'Articles.published', 'Articles.created',
162+
'Articles.slug',
163+
];
164+
$query = $query
165+
->select($columns)
166+
->distinct($columns);
167+
168+
if (empty($tags)) {
169+
// If there are no tags provided, find articles that have no tags.
170+
$query->leftJoinWith('Tags')
171+
->where(['Tags.title IS' => null]);
172+
} else {
173+
// Find articles that have one or more of the provided tags.
174+
$query->innerJoinWith('Tags')
175+
->where(['Tags.title IN' => $tags]);
176+
}
177+
178+
return $query->groupBy(['Articles.id']);
179+
}
109180
}

0 commit comments

Comments
 (0)