admin管理员组文章数量:1129201
I’m working on a Laravel (and FilamentPHP) project and noticed a discrepancy between the old “magic” accessor method and the new class-based Attribute accessor introduced in Laravel 8.40+. Specifically, when I use:
Model::query()->pluck('name', 'id')
- The old magic accessor (
getNameAttribute()
) applieducwords()
to the plucked values. - The new class-based accessor (
protected function name(): Attribute
) does not apply the transformation when plucking.
Here’s a simplified example of my model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use App\Enums\SomeEnumStatusState;
class MyModel extends Model
{
// OLD Magic Accessor (works with pluck)
// public function getNameAttribute($value)
// {
// return ucwords(strtolower($value));
// }
// NEW Class-based Accessor (not applying with pluck)
protected function name(): Attribute
{
return Attribute::make(
get: fn ($value) => ucwords(strtolower($value))
);
}
public function scopeOpen(Builder $query): Builder
{
return $query->where('state', SomeEnumStatusState::OPEN);
}
protected function casts(): array
{
return [
'state' => SomeEnumStatusState::class,
];
}
}
And in my FilamentPHP Resource:
Forms\Components\Select::make('model_id')
->label('Status')
->relationship('modelRelationship', 'name')
->options(MyModel::query()->pluck('name', 'id')),
My questions are:
Why does
Model::query()->pluck('name', 'id')
return the raw database values when using the new class-basedname(): Attribute
but returns transformed values under the oldgetNameAttribute()
method?Is this the intended behavior, or am I missing something about how
pluck()
interacts with the newer Attribute accessors?What is the recommended or “best practice” way to retrieve transformed attribute values (especially with FilamentPHP) under the new accessor approach?
Any insights, explanations, or code examples showing how to ensure pluck()
respects the class-based accessor would be greatly appreciated!
I’m working on a Laravel (and FilamentPHP) project and noticed a discrepancy between the old “magic” accessor method and the new class-based Attribute accessor introduced in Laravel 8.40+. Specifically, when I use:
Model::query()->pluck('name', 'id')
- The old magic accessor (
getNameAttribute()
) applieducwords()
to the plucked values. - The new class-based accessor (
protected function name(): Attribute
) does not apply the transformation when plucking.
Here’s a simplified example of my model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use App\Enums\SomeEnumStatusState;
class MyModel extends Model
{
// OLD Magic Accessor (works with pluck)
// public function getNameAttribute($value)
// {
// return ucwords(strtolower($value));
// }
// NEW Class-based Accessor (not applying with pluck)
protected function name(): Attribute
{
return Attribute::make(
get: fn ($value) => ucwords(strtolower($value))
);
}
public function scopeOpen(Builder $query): Builder
{
return $query->where('state', SomeEnumStatusState::OPEN);
}
protected function casts(): array
{
return [
'state' => SomeEnumStatusState::class,
];
}
}
And in my FilamentPHP Resource:
Forms\Components\Select::make('model_id')
->label('Status')
->relationship('modelRelationship', 'name')
->options(MyModel::query()->pluck('name', 'id')),
My questions are:
Why does
Model::query()->pluck('name', 'id')
return the raw database values when using the new class-basedname(): Attribute
but returns transformed values under the oldgetNameAttribute()
method?Is this the intended behavior, or am I missing something about how
pluck()
interacts with the newer Attribute accessors?What is the recommended or “best practice” way to retrieve transformed attribute values (especially with FilamentPHP) under the new accessor approach?
Any insights, explanations, or code examples showing how to ensure pluck()
respects the class-based accessor would be greatly appreciated!
2 Answers
Reset to default 1Looking into the vendor files for this, the pluck
method respects mutators as follows:
// If the model has a mutator for the requested column, we will spin through
// the results and mutate the values so that the mutated version of these
// columns are returned as you would expect from these Eloquent models.
if (! $this->model->hasGetMutator($column) &&
! $this->model->hasCast($column) &&
! in_array($column, $this->model->getDates())) {
return $results;
}
Taking a dive deeper into the hasGetMutator
method we can see that this will only check for method names using the previous magic methods.
public function hasGetMutator($key)
{
return method_exists($this, 'get'.Str::studly($key).'Attribute');
}
One way i've found to get around this would be to specify the attribute on the $appends
array (relevant docs) on the model and then ->get()
before ->pluck(...)
like so:
Model::query()->get()->pluck('name', 'id')
AFAIK, pluck doesn't access your model but just gets the columns (and only those) directly from the database. This has a significant performance advantage, since much less data is retrieved from the database, and Laravel doesn't need to construct the model objects. The disadvantage however, is that you get only the literal database values which may or may not suffice your needs.
Hence when using pluck on the query, your model isn't constructed/booted and thus your accessor isn't used. If you want to retrieve those attribute values, you can do something like
MyModel::all()->map(fn (MyModel $record) => [$record->id, $record->name])
This retrieves all records of this model from the database, constructs all the models and puts it in a collection and then gets the id and name.
本文标签:
版权声明:本文标题:php - Why don’t the new class-based Laravel Eloquent accessors apply with `->pluck('name')` as they did w 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736739038a1950408.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
MyModel::query()->select('name', 'id')->get()->pluck('name', 'id')
– kris gjika Commented Jan 8 at 11:16