phplaravelclassstatic-methodslaravel-facade

How to make Laravel Facades use their own attributes?


I created a CalendarResource class, that I want to call statically using a Facade:

<?php

namespace App\Models\Shared;

class CalendarResource
{
    public function __construct(
        public string $name = '',
        public string $color = '#000000',
        public string $textColor = '#ffffff'
    ) {
    }

    public function toArray(): array
    {
        return [
            'id' => $this->name,
            'name' => $this->name,
            'color' => $this->color,
            'textColor' => $this->textColor,
        ];
    }

    public function setColor(string $color): self
    {
        $this->color = $color;

        return $this;
    }

    public function setTextColor(string $textColor): self
    {
        $this->textColor = $textColor;

        return $this;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }
}

This is my Facade:

<?php

namespace App\Facades;

use Illuminate\Support\Facades\Facade;

/**
 * @method static \App\Models\Shared\CalendarEvent setName(string $title)
 * @method static \App\Models\Shared\CalendarEvent setColor(string $color)
 * @method static \App\Models\Shared\CalendarEvent setTextColor(string $color)
 * @method static \App\Models\Shared\CalendarEvent toArray()
 *
 * @see \App\Models\Shared\CalenderResource
 */
class CalendarResource extends Facade
{
    protected static function getFacadeAccessor(): string
    {
        return \App\Models\Shared\CalendarResource::class;
    }
}

Now I am using the facade like this:

use App\Facades\CalendarResource as Resource;

$r1 = Resource::setColor('red')->setName('Test')->toArray();
$r2 = Resource::setColor('blue')->setName('Foo')->toArray();

echo $r1['color'].PHP_EOL;
echo $r2['color'].PHP_EOL;

What outputs:

red
blue

This is fine but now my confusion starts: If I don't set the setColor('blue') on $r2 I get

red
red

And this is unwanted. I expect to get

red
#000000

It seems like the $r2 is using the values set for $r1. I want every Resource be "it's own" class. What do I need to change the get that expected behaviour and how can I make the name mandatory? If I remove = '' I get Unresolvable dependency resolving [Parameter #0 [ <required> string $name ]] in class App\Models\Shared\CalendarResource.


Solution

  • The Laravel facade functionality is going to create one instance of the class and store it in the container. Every reference to the facade will reference the same instance.

    You're confusing Laravel's facades with just plain static method calls. What you're looking for is a "static factory method". Laravel facades are not involved with this.

    For example, get rid of all the facade stuff and add this function to your shared model class:

    public static function withName(string $name): self
    {
        return new self($name);
    }
    

    With this, every call to withName() will return a new instance that has the name you specify. You can also remove the default value for the name from the constructor now.

    Test your code:

    use App\Models\Shared\CalendarResource;
    
    $r1 = CalendarResource::withName('Test')->setColor('red')->toArray();
    $r2 = CalendarResource::withName('Foo')->toArray();
    
    echo $r1['color'].PHP_EOL; // red
    echo $r2['color'].PHP_EOL; // #000000