2

I think my brain isn't functioning today as I can't seem to get my head around this one.

I have a class that has a data array, for example -

class Testing {

    protected $fillable = ['questions.*.checked'];

    protected $data = [
        'active' => true,
        'questions' => [
            [
                'question' => 'This is the first question',
                'checked' => true,
            ],
            [
                'question' => 'This is the second question',
                'checked' => false,
            ]
         ]
    ];

    public function fill(array $attributes = []) {
        // take our attributes array, check if the key exists in
        // fillable, and if it does then populate our $data property
    }

}

What I would like to do is that if I passed the following array to the Testing::fill() method, it'd update only the respective attributes that are considered fillable.

E.g., passing the following array

[
    'active' => false,
    'questions' => [
        [
            'question' => 'This is the first question',
            'checked' => true,
        ],
        [
            'question' => 'This is the second question',
            'checked' => true,
        ]
    ]
]

Would only modify the checked flags on the object, and everything else would be ignored - only marking the properties $data attribute questions.*.checked as true

I feel like there is a solution using Laravel's helpers but I just can't seem to come to it, or perhaps I am going the wrong way about it...

Ultimately, I just want some level of sanitisation so that when the entire structure is posted back to the objects fill method, only certain items can actually get updated (just like Laravel's fill method, just deeper with dynamic values). The problem is what is actually contained within $data is dynamic, so there might be one question, there might be 100...

1 Answer 1

1

Ok, I've come up with a solution which does the job but I was hoping there was something a touch more Laravel centric out there.


protected function isFillable($key)
{
    // loop through our objects fillables
    foreach ($this->fillable as $fillable) {

        // determine if we have a match
        if ($fillable === $key
            || preg_match('/' . str_replace('*', '([0-9A-Z]+)', $fillable) . '/i', $key)
        ) {
            return true;
        }
    }

    // return false by default
    return false;
}

public function fill(array $attributes = [])
{
    // convert our attributes to dot notation
    $attributes = Arr::dot($attributes);

    // loop through each attribute
    foreach ($attributes as $key => $value) {

        // check our attribute is fillable and already exists...
        if ($this->isFillable($key)
            && !(Arr::get($this->data, $key, 'void') === 'void')
        ) {

            // set our attribute against our data
            Arr::set($this->data, $key, $value);
        }
    }

    // return ourself
    return $this;
}

So, in the above, when I call fill() method, I'm converting all of the attributes to Laravel's dot notation using Arr::dot(). This makes the array easier to loop through and allows me to perform the kind of checks I'm looking for.

I've then created an isFillable() method to determine if the attributes key exists within our objects $fillable property. If there's a wildcard involved, it converts the stars (*) to regular expressions and then checks if there is a match. Before performing the regex, it performs a basic comparison check, ideally hoping to bypass the regex and improve overall performance where possible.

So, finally, if our attribute is fillable, and we are able to get the value from our data array already, then we'll let this value be updated using Arr::set()

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.