-1

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.

JanBoehmer
  • 395
  • 3
  • 14
  • It's probably not good architecture/strategy for using static values. Check [this](https://www.php.net/manual/en/language.oop5.static.php#51640) example from docs. – Tpojka May 18 '23 at 11:02
  • A already see that it's not good, as it not working as intended :D But what would be the correct strategy? The main reason I am using a facade, is because `Resource::setName('Test');` looks cleaner than `(new Resource)->setName('Test');`, following the sentence of the Laravel doc: `[...] providing the benefit of a terse, expressive syntax [...]`. – JanBoehmer May 18 '23 at 11:38
  • "`Resource::setName('Test');` looks cleaner than `(new Resource)->setName('Test');`" seems very subjective. I am always for functionality over aesthetic. Or "Make it work, Make it right, Make it fast". – Tpojka May 18 '23 at 12:00
  • You can try creating a static method that instantiates the object, where you can chain it to be more eloquent. `public static function get() { return new static(); }` `Resources::get()->setName('Test');` – Aaron T May 18 '23 at 12:27
  • To get that right: My issue is the usage of the static methods because the belong the class itself and not to the object instance? – JanBoehmer May 18 '23 at 14:34
  • You can think about facades as singletons. All you need is creating of two independent objects of Resource class – lezhni May 18 '23 at 14:36

1 Answers1

2

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
patricus
  • 59,488
  • 15
  • 143
  • 145
  • 1
    I will also add that the namespace of the original class is also wrong, should not be inside Models as that is NOT a model but a custom class that is related to nothing (relative to laravel), so ot will just confused new devs – matiaslauriti May 18 '23 at 15:02
  • That means the `withName` method is the "entry point" to a static syntax by instantiating a new object every time as the rest stays non static? The only downside of this, would be, that the order of the methods is important – JanBoehmer May 18 '23 at 15:25
  • 1
    @JanBoehmer Correct, but I only wrote it that way because your question asked about making the name mandatory. If the name is mandatory, you don't want to be able to create an object without a name, so the entry point would be something requiring a name. If that is not a requirement, then you can have a simple `make()` or `new()` method or something similar that all it does is instantiate a new object. In that instance, though, you will have no way to enforce a name. – patricus May 18 '23 at 15:53