Courses

Eloquent: The Expert Level (Laravel 9)

Attributes: Accessors and Mutators with New/Old Syntax

Previous: Model Observers and Their Methods
avatar

In single case (get OR set attribute) the old way is simpler, more clear and easier to read. However in mixed case the new way looks more logical and better.

👍 4
👀 1
avatar

I like the new way better

avatar
You can use Markdown
avatar

protected function startDate(): Attribute

{
    return Attribute::make(
        set: fn ($value) => Carbon::createFromFormat('d/m/Y', $value)->format(format: 'Y-m-d'),
        get: fn ($value) => Carbon::createFromFormat('Y-m-d', $value)->format(format: 'd/m/Y'),
    );
}

When executing above code, i am getting below error :

Carbon \Exceptions \InvalidFormatException

The separation symbol could not be found Unexpected data found. Trailing data

Please guide

avatar

Because probably you provided date in wrong format? It should be d/m/Y, like 23/12/1984. What date did you enter?

avatar
created a form and selecting the date in dd-mm-yyyy format as seen here
avatar

In the date picker select you posted here, I don't see dd-mm-yyy, I see yyyy-mm-dd

Can you give the code to the repository and I would debug myself and then could answer? Put it on GitHub and invite me, username PovilasKorop

avatar

Tried following Steps after installation of laravel project with starter kit : breeze

Step 1 : added birth_date in users table

    Schema::create('users', function (Blueprint $table) {
       ...
        $table->date('birth_date')->nullable();
        --
    });
			
Step 2: added birth_date in User model
		
		protected $fillable=[...'birth_date'];

Step 3: added method in User Model for accessor and mutator

 protected function birthDate(): Attribute
{
    return Attribute::make(
        get: fn ($value) => Carbon::createFromFormat('Y-m-d', $value)->format(format: 'm/d/Y'),
        set: fn ($value) => Carbon::createFromFormat('m/d/Y', $value)->format(format: 'Y-m-d'),

    );
}

Step 4 : modified resources\views\auth\register.blade.php to add birth_date

     <div class="mt-4">
        <x-input-label for="birth_date" :value="__('birth_date')" />
        <x-text-input id="birth_date" class="block mt-1 w-full" type="date" name="birth_date" :value="old('birth_date')" required autocomplete="birthdate" />
        <x-input-error :messages="$errors->get('birth_date')" class="mt-2" />
    </div>

Step 5 : after migration & run project, when tried to register user by clicking on Register button, following error occured

Carbon \Exceptions \ InvalidFormatException

The separation symbol could not be found Unexpected data found. Trailing data

When I comment below method everthing is fine and birth_date get saved in database

// protected function birthDate(): Attribute{ // .... // }

Also note, it works under tinker but not on Form

Hope you understood the problem. please guide on this.

avatar

That's exactly the point, your're answering your own question: "it works under tinker but not on form".

Which means that your FRONT-END date picker does not give the correct date format. That x-text-input type="date" should provide the dates in the format you provided.

But also, I see you were mentioning the format dd-mm-yyyy earlier, but in the code of this latest comment, in the set part, I see createFromFormat('m/d/Y'), maybe that's the reason? m/d/Y instead of d/m/Y?

avatar

Hi Povilas, Thanks for quick response.

Please note, that the date format seen in form is based on local system date setting.

By keeping system date format either dd-mm-yyyy or mm-dd-yyyy error does not change.

Also tried after changing createFromFormat('m/d/Y') to createFromFormat('d/m/Y'). Error remains same.

Carbon\Exceptions \InvalidFormatException

The separation symbol could not be found Unexpected data found. Trailing data

My objective is to set date entry format as dd/mm/yyyy, irrespective of user's system date.

avatar

I think that you are mixing some things up. Let's clear them out and hopefully understand the issue better:

  1. You are using input type=date which is controlled by the browser. You cannot decide the date format there, it is dependant on the format that the browser has. If you need to change the format displayed there to ignore user settings - you need to use a 3rd party JS datetime picker.
  2. You are assuing that the date is coming to you in m/d/Y format, but in reality it is not. Please dump the request to see what format the date returns for you (probably like Y-m-d)
  3. Your date is nullable, yet your GET method does not account for that. You are trying to parse a date from null. Add an if condition to only parse if the value is there, and skip it if there's no value.

Hope these will help you understand what's going on!

avatar

Thanks Modestas, I changed the code to skip null value as follows :

protected function birthDate(): Attribute

{
    return Attribute::make(
        get: fn ($value) => Carbon::createFromFormat('Y-m-d', $value)->format(format: 'd/m/Y'),
        set: $this->value ? fn ($value) => Carbon::createFromFormat('d/m/Y', $value)->format(format: 'Y-m-d') : null,
    );
}

But I could not figureout the practical application of the date mutator and accessor example shown by Povilas

avatar

Hm, could you explain then what is not working here correctly?

ps. You have added this to the setter, but not to the getter. Your database can still have null value and you will try to convert it :)

avatar

By adding condition to setter, error got eliminated, which was seen on form saving. So far, there is no error on getter, so condition not added.

I am in learning phase.

I want to show birth date on form in dd/mm/yyyy irrespective of browser format. Can it be done using getter ?

avatar
You can use Markdown
avatar
You can use Markdown