Skip to content

Commit 3ac91f0

Browse files
jackmcdadeclaudejasonvarga
authored
[6.x] Add support for filtering conditions in Assets Tag (#13936)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Jason Varga <jason@pixelfear.com>
1 parent ece1486 commit 3ac91f0

3 files changed

Lines changed: 391 additions & 35 deletions

File tree

src/Assets/Asset.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ class Asset implements Arrayable, ArrayAccess, AssetContract, Augmentable, Conta
6666
resolveGqlValue as traitResolveGqlValue;
6767
}
6868

69+
const AUDIO_EXTENSIONS = ['aac', 'flac', 'm4a', 'mp3', 'ogg', 'wav'];
70+
const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif'];
71+
const VIDEO_EXTENSIONS = ['h264', 'mp4', 'm4v', 'ogv', 'webm', 'mov'];
72+
6973
protected $container;
7074
protected $path;
7175
protected $meta;
@@ -471,7 +475,7 @@ public function manipulate($params = null)
471475
*/
472476
public function isAudio()
473477
{
474-
return $this->extensionIsOneOf(['aac', 'flac', 'm4a', 'mp3', 'ogg', 'wav']);
478+
return $this->extensionIsOneOf(self::AUDIO_EXTENSIONS);
475479
}
476480

477481
/**
@@ -504,7 +508,7 @@ public function isPreviewable()
504508
*/
505509
public function isImage()
506510
{
507-
return $this->extensionIsOneOf(['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif']);
511+
return $this->extensionIsOneOf(self::IMAGE_EXTENSIONS);
508512
}
509513

510514
/**
@@ -524,7 +528,7 @@ public function isSvg()
524528
*/
525529
public function isVideo()
526530
{
527-
return $this->extensionIsOneOf(['h264', 'mp4', 'm4v', 'ogv', 'webm', 'mov']);
531+
return $this->extensionIsOneOf(self::VIDEO_EXTENSIONS);
528532
}
529533

530534
/**

src/Tags/Assets.php

Lines changed: 101 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,22 @@
22

33
namespace Statamic\Tags;
44

5+
use Statamic\Assets\Asset as AssetModel;
56
use Statamic\Assets\AssetCollection;
67
use Statamic\Contracts\Query\Builder;
78
use Statamic\Facades\Asset;
89
use Statamic\Facades\AssetContainer;
910
use Statamic\Facades\Entry;
11+
use Statamic\Facades\Pattern;
1012
use Statamic\Fields\Value;
1113
use Statamic\Support\Arr;
1214

1315
class Assets extends Tags
1416
{
17+
use Concerns\QueriesConditions,
18+
Concerns\QueriesOrderBys,
19+
Concerns\QueriesScopes;
20+
1521
/**
1622
* @var AssetCollection
1723
*/
@@ -40,7 +46,7 @@ public function wildcard($method)
4046

4147
$this->assets = (new AssetCollection([$value]))->flatten();
4248

43-
return $this->output();
49+
return $this->outputCollection($this->assets);
4450
}
4551

4652
if ($value instanceof Value) {
@@ -66,15 +72,17 @@ public function index()
6672
$path = $this->params->get('path');
6773
$collection = $this->params->get('collection');
6874

69-
$this->assets = $collection
70-
? $this->assetsFromCollection($collection)
71-
: $this->assetsFromContainer($id, $path);
75+
if ($collection) {
76+
return $this->outputCollection($this->assetsFromCollection($collection));
77+
}
78+
79+
$this->assets = $this->assetsFromContainer($id, $path);
7280

7381
if ($this->assets->isEmpty()) {
7482
return $this->parseNoResults();
7583
}
7684

77-
return $this->output();
85+
return $this->assets;
7886
}
7987

8088
protected function assetsFromContainer($id, $path)
@@ -95,9 +103,29 @@ protected function assetsFromContainer($id, $path)
95103
return collect();
96104
}
97105

98-
$assets = $container->assets($this->params->get('folder'), $this->params->get('recursive', false));
106+
$query = $container->queryAssets();
107+
108+
$this->queryFolder($query);
109+
$this->queryType($query);
110+
$this->queryConditions($query);
111+
$this->queryScopes($query);
112+
$this->queryOrderBys($query);
113+
114+
if ($this->params->get('not_in')) {
115+
$assets = $this->filterNotIn($query->get());
116+
117+
return $this->limitCollection($assets);
118+
}
119+
120+
if ($limit = $this->params->int('limit')) {
121+
$query->limit($limit);
122+
}
99123

100-
return $this->filterByType($assets);
124+
if ($offset = $this->params->int('offset')) {
125+
$query->offset($offset);
126+
}
127+
128+
return $query->get();
101129
}
102130

103131
protected function assetsFromCollection($collection)
@@ -143,6 +171,10 @@ protected function filterByType($value)
143171
}
144172

145173
return $value->filter(function ($value) use ($type) {
174+
if ($type === 'audio') {
175+
return $value->isAudio();
176+
}
177+
146178
if ($type === 'image') {
147179
return $value->isImage();
148180
}
@@ -161,18 +193,17 @@ protected function filterByType($value)
161193

162194
/**
163195
* Filter out assets from a requested folder.
164-
*
165-
* @return void
166196
*/
167-
private function filterNotIn()
197+
private function filterNotIn($assets)
168198
{
169-
if ($not_in = $this->params->get('not_in')) {
170-
$regex = '#^('.$not_in.')#';
171-
172-
$this->assets = $this->assets->reject(function ($path) use ($regex) {
173-
return preg_match($regex, $path);
174-
});
199+
if (! $not_in = $this->params->get('not_in')) {
200+
return $assets;
175201
}
202+
203+
$regex = '#^('.$not_in.')#';
204+
205+
// Checking against path for backwards compatibility. Technically folder would be more correct.
206+
return $assets->reject(fn ($asset) => preg_match($regex, $asset->path()));
176207
}
177208

178209
/**
@@ -204,36 +235,74 @@ protected function assets($urls)
204235
];
205236
});
206237

207-
return $this->output();
238+
return $this->outputCollection($this->assets);
208239
}
209240

210-
private function output()
241+
private function outputCollection($assets)
211242
{
212-
$this->filterNotIn();
243+
$this->assets = $this->filterNotIn($assets);
213244

214-
$this->sort();
215-
$this->limit();
245+
if ($sort = $this->params->get('sort')) {
246+
$this->assets = $this->assets->multisort($sort);
247+
}
248+
249+
$this->assets = $this->limitCollection($this->assets);
250+
251+
if ($this->assets->isEmpty()) {
252+
return $this->parseNoResults();
253+
}
216254

217255
return $this->assets;
218256
}
219257

220-
private function sort()
258+
private function limitCollection($assets)
221259
{
222-
if ($sort = $this->params->get('sort')) {
223-
$this->assets = $this->assets->multisort($sort);
260+
$limit = $this->params->int('limit');
261+
$limit = ($limit == 0) ? $assets->count() : $limit;
262+
$offset = $this->params->int('offset');
263+
264+
return $assets->splice($offset, $limit);
265+
}
266+
267+
protected function queryType($query)
268+
{
269+
$type = $this->params->get('type');
270+
271+
if (! $type) {
272+
return;
224273
}
274+
275+
$extensions = match ($type) {
276+
'audio' => AssetModel::AUDIO_EXTENSIONS,
277+
'image' => AssetModel::IMAGE_EXTENSIONS,
278+
'svg' => ['svg'],
279+
'video' => AssetModel::VIDEO_EXTENSIONS,
280+
default => [],
281+
};
282+
283+
$query->whereIn('extension', $extensions);
225284
}
226285

227-
/**
228-
* Limit and offset the asset collection.
229-
*/
230-
private function limit()
286+
protected function queryFolder($query)
231287
{
232-
$limit = $this->params->int('limit');
233-
$limit = ($limit == 0) ? $this->assets->count() : $limit;
234-
$offset = $this->params->int('offset');
288+
$folder = $this->params->get('folder');
289+
$recursive = $this->params->get('recursive', false);
290+
291+
if ($folder === '/' && $recursive) {
292+
$folder = null;
293+
}
294+
295+
if ($folder === null) {
296+
return;
297+
}
298+
299+
if ($recursive) {
300+
$query->where('path', 'like', Pattern::sqlLikeQuote($folder).'/%');
301+
302+
return;
303+
}
235304

236-
$this->assets = $this->assets->splice($offset, $limit);
305+
$query->where('folder', $folder);
237306
}
238307

239308
private function isAssetsFieldValue($value)

0 commit comments

Comments
 (0)