#Parent constructor requires arguments

28 messages · Page 1 of 1 (latest)

nocturne hornet
#

Hi guys, so I ran in to a little issue, I understand why it happens but I've just come here to ask for the best solution to this.
I have a constructor like:

/**
 * Client constructor.
 */
public function __construct(SettingsRepositoryInterface $set)
{
    $this->client = new Client($set);
}```

to define `$this->client` having the stripe api secret by default, this is in a class `Client`, in all classes using the client I extend the Client class to use `$this->client` in them without having to make a complete construct with the settingsrepositoty and stuff. However, the issue here, is that when I have a child constructor in any of the classes extending the Client class, I am using `parent::__construct();` to get `$this->client` but that requires one argument `$set`, what is the best way to achieve this without having to define `$set` in every extending class having a constructor?
strange fjord
#

Injecting dependencies is kinda what the service container is for.

#

And you only need to define constructors in child classes if you’re actually doing anything in addition. You don’t need to define constructors in child classes if all you do is then just call the parent one.

#

The parent constructor will be called by default.

nocturne hornet
nocturne hornet
strange fjord
nocturne hornet
# strange fjord That doesn’t really make any sense. Can you show examples of these classes, and ...

otherways I would be in a unlimited loop of extensions.

Il try to explain it as good as I can:

  • I have 1 main class not extending anything called Client, it includes the parent constructor which I sent above.
  • Then I have a subclass named productService extending the main class Client, which doesn't have a child constructor as it doesn't need anything extra to be defined.
  • And last I have a subclass extending the main class Client, but it has a child constructor as it needs productService to be defined.
#

If I would add $this->productService inside the main Client class, it would end up in a memory leak looping it self, as productService it self is importing it self

#

so I need to include it in to a child constructor, which should also get the parent constructor for $this->client to be defined

strange fjord
#

Sounds like you need to think about your classes and their hierarchy a bit more. My naive view is that: services should have clients injected; not extend client classes.

#

To me, a client is a class that facilitates transport of data. And a service would then make use of a client to do things, i.e. use a client to retrieve and persist products.

nocturne hornet
#

aa like that, well in that case it is my bad word choosing and the main Client class should be named something like Service as it's being a default definition for all other services injecting and defining certain services like $this->client

strange fjord
#

Yeah, like I say, would be much easier if you could just show an example of these classes.

#

But there’s not really anything to do to “work around” this.

#

If you define something in a parent constructor, then you push that requirement to every child class as well.

nocturne hornet
# strange fjord Yeah, like I say, would be much easier if you could just show an example of thes...

well there isn't really much code:

public $client;
public $set;

/**
 * Client constructor.
 */
public function __construct(SettingsRepositoryInterface $set)
{
    $this->client = new StripeClient($set->get('settings::billing::stripe_secret'));
}``` is all in the `Service` class
and 
```php
/**
 * PaymentMethodService constructor.
 */
public function __construct(
    public CustomerService $customers
) {
    parent::__construct();
}```

is everything in the service which is having the child construct.
nocturne hornet
strange fjord
#

Is this some sort of multi-tenant system if you’re creating Stripe clients from some sort of settings repository?

nocturne hornet
strange fjord
#

So use the container to inject well-configured client instances instead of explicitly instantiating in them in constructors.

#

Your service classes should just be type-hinting what it needs, i.e. StripeClient

#

You can then bind a tenant-specific instance in the container using middleware or something after you’ve resolved your tenant.

#
class SomeService
{
    protected $stripe;

    public function __construct(StripeClient $stripe)
    {
        $this->stripe = $stripe;
    }
}
class ResolveTenantServices
{
    protected $container;

    public function __construct(Container $container)
    {
        $this->container = $container;
    }

    public function handle(Request $request, Closure $next)
    {
        // Get tenant from route parameter or whatever
        $tenant = $request->route('tenant');

        // Now bind tenant-specific services to container
        $this->container->bind(StripeClient::class, function () use ($tenant) {
            return new StripeClient([
                'api_key' => $tenant->settings->stripe_api_key,
            ]);
        });

        return $next($request);
    }
}
#

Obviously you’ll need to update the code based on how you resolve your tenant and their settings, but that’s the basic gist.