Use Behaviors to create custom query scopes for element queries? #13636
-
When working with Eloquent models in Laravel you can create reusable "scopes". For instance: <?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Scope a query to only include popular users.
*/
public function scopePopular(Builder $query): void
{
$query->where('votes', '>', 100);
}
} And then query this subset using: User::popular()->get(); I'd like to create a Craft/Yii equivalent, but wasn't quite sure how to do that. It seems behaviors might be a good fit, but wanted to see if I'm way off base on this. Here's what I have: <?php
namespace modules\foo;
use yii\base\Event;
use craft\elements\db\EntryQuery;
use craft\events\DefineBehaviorsEvent;
use modules\foo\behaviors\QueryBehavior;
class Module extends yii\base\Module
{
public function init()
{
Event::on(
EntryQuery::class,
EntryQuery::EVENT_DEFINE_BEHAVIORS,
function (DefineBehaviorsEvent $event) {
// Avoid binding to non EntryQuery classes
if (!$event->sender instanceof EntryQuery) {
return false;
}
$event->sender->attachBehaviors([
QueryBehavior::class
]);
}
);
}
} And then here is my behavior: <?php
namespace modules\foo\behaviors;
use craft\elements\db\EntryQuery;
class QueryBehavior extends \yii\base\Behavior
{
public function popular(): EntryQuery
{
/**
* @var EntryQuery $query
*/
$query = $this->owner;
return $query->where('votes > 100');
}
} And then my template: {{ craft.entries.section('posts').popular().all }} Does this make sense? I've used behaviors a number of times to create computed properties on entry records using their field data. But I've not yet created scopes with them to see how this might work and what pitfalls might exist. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 2 replies
-
Maybe it's better to use I assume, in /Karla |
Beta Was this translation helpful? Give feedback.
-
Yep, this is definitely a valid use case for behaviors. Your event handler could be simplified – no need to ensure it’s an use craft\elements\db\UserQuery;
use craft\events\DefineBehaviorsEvent;
use yii\base\Event;
Event::on(
UserQuery::class,
UserQuery::EVENT_DEFINE_BEHAVIORS,
function(DefineBehaviorsEvent $event) {
$event->behaviors[] = QueryBehavior::class;
}
); And as @wsydney76 said, you should be using |
Beta Was this translation helpful? Give feedback.
Yep, this is definitely a valid use case for behaviors.
Your event handler could be simplified – no need to ensure it’s an
EntryQuery
as you’re already registering a class-level event forEntryQuery
instances, and you can set your behavior on$event->behaviors
rather than manually attaching it.And as @wsydney76 said, you should be using
andWhere()
within your behavior, so it doesn’t override any conditions already set on t…