How to pass attributes in Laravel scope using name argument (PHP8 feature)

91 views Asked by At

In my Setting model. I have a local scope that accepts $data array argument. The $data contains two keys group and property and any of them can be null as you can see in my query.

 public function scopeSettingFilters($query, array $data)
    {
        $query->when(!empty($data['group']), fn($q) => $q->where("group", $data["group"]));

        $query->when(!empty($data['property']), function ($q) use ($data) {
            $property = $data['property'];

            if (str_contains($property, '.')) {
                [$group, $property] = $this->splitKey($data['property']);
                $q->where(["group" => $group, "name" => $property]);

            } else {
                $q->where("name", $property);
            }
        });

        return $query;
    }

Now I want to convert $data array keys to Named Arguments for better code understanding and one major reason for conversion is, that currently the group is accepting only string but now it is either a string or array.

My Setting Table

id user_id group property value
1 1 notifications event_email true
1 1 onboarding welcome_video_watched false

Note: I'm using Laravel 9 Version

Expected Syntax:

After making the required changes I would able to call Laravel query scope using named argument something like that:

Setting::settingFilters(group: "onboarding")->get()    //filter single group

Setting::settingFilters(group: ["onboarding", "notifications"])->get()    //filter multiple groups
2

There are 2 answers

0
Azan Korejo On

If you want to refactor your code to use named arguments, you can change the function signature of scopeSettingFilters to accept named arguments instead of a single $data array. You can set default values for the arguments to null to make them optional.

Here's an example that shows how you could refactor your code:

public function scopeSettingFilters($query, string|array|null $group = null, string|null $property = null)
{
    // Filter by 'group'
    $query->when(!empty($group), function ($q) use ($group) {
        if (is_array($group)) {
            $q->whereIn('group', $group);
        } else {
            $q->where('group', $group);
        }
    });

    // Filter by 'property'
    $query->when(!empty($property), function ($q) use ($property) {
        if (str_contains($property, '.')) {
            [$group, $property] = $this->splitKey($property);
            $q->where(['group' => $group, 'name' => $property]);
        } else {
            $q->where('name', $property);
        }
    });

    return $query;
}

Key changes:

The function now accepts named arguments $group and $property, both of which have default values set to null.

Inside the lambda function for the when() method, I've added a check for $group being an array. If it is an array, it uses whereIn to filter by multiple group names; otherwise, it uses where as you did before.

Now you can call this scope with named arguments like so:

// With a single group name
$query->settingFilters(group: 'someGroup');

// With multiple group names
$query->settingFilters(group: ['group1', 'group2']);

// With a property
$query->settingFilters(property: 'someProperty');

// With both a group and a property
$query->settingFilters(group: 'someGroup', property: 'someProperty');
0
M-Khawar On

I had fixed it using following code:

 public function settingFilters(string|array $group = null, string $property = null)
    {
        $query = $this->settings();

        $group = is_array($group) ? $group : [$group];


        $query->when(!empty($group), fn($q) => $q->whereIn("group", $group));

        $query->when(!empty($data['property']), function ($q) use ($property) {

            if (str_contains($property, '.')) {
                [$group, $property] = $this->splitKey($property);
                $q->where(["group" => $group, "name" => $property]);

            } else {
                $q->where("name", $property);
            }
        });

        return $query;
    }