Symfony 6 #[Autowire] VS #[Target] attributes - php

From symfony docs, it looks like that we can use #[Target('service_id')] attribute in order to specify id of service to inject.
From other side, one of the new symfony 6 features is #[Autowire(service: 'service_id')] attribute, which describes what to wire the argument to.
At first glance, #[Target] is just another way of doing stuff, or do I miss something?

If using service_id, than they should be acting in same way - telling to inject service that has ID service_id.
Difference comes when you need not service, but something different - e.g. scalar. Then you can use only #[Autowire] attribute:
class FooClass
{
public function __construct(
#[Target(name: 'app.logger.on_screen')]
private LoggerInterface $logger,
#[Autowire(expression: 'service("App\\My\\Service").generateProxyName()')]
private string $proxyName,
) {
}
}
here you could not use #[Target], because you do not inject service, but string value

Related

Is the newInstance method fast with little footprint to use in PHP 8 ReflectionAttribute class?

In PHP 8, the class ReflectionAttribute is introduced. It is something like annotations in Java, Typescripts and etc. Well every time you wanna use the attribute of a property, for example, you must do as follow:
$attributeReflection = ...;
...
$attribute = $attributeReflection->newInstance();
$message = $attribute->getMessage();
Where the $attributeReflection is an instance of ReflectionAttribute. And the attribute itself is as follow:
#[Attribute(Attribute::ALL)]
class MyAttribute{
public function __construct(
public ?string $message = ''
){}
}
And is used as follow for a property
class Foo{
#[MyAttribute("my message")
public ?string $id = null;
}
As you can see, every time I wanna get the message of the attribute, I have to create a new instance of it. While the message is never changed for this case.
I`m looking for a way to avoid a new instance and use a shared one.
Is it possible?
It looks like nobody has written up the documentation for the ReflectionAttribute class yet, but you can see a summary in the feature proposal for Attributes, which lists its methods:
class ReflectionAttribute
{
public function getName(): string { ... }
public function getArguments(): array { ... }
public function newInstance(): object { ... }
}
The key here is that you can access the arguments without calling the constructor - indeed, the attribute name doesn't even need to be a valid class name, since it will not be autoloaded until newInstance is called.
So in your example, you can simply say $message = $attributeReflection->getArguments()[0], or write a custom factory that merges instances with the same message, etc.
On the other hand, beware of premature optimisation: only spend time making this more complex if profiling tells you that this is actually a significant cost in your application. You may well find that the reflection itself has a much higher cost than this constructor call, and decide to run the whole thing in a build script, saving the information needed at run-time into some kind of cache.

DI - How to refactor constructor with lots of dependencies

I'm fairly new to dependency injection, and I've come upon a situation where I have a class which is basically just functional programming -- one method in it (createPayment) requires 9 dependencies, and the other method (paypalApiEndpoint) only requires 1 of those dependencies:
class Paypal
{
private $restAPIClient;
private $db;
private $logger;
private $paypalGateway;
private $paymentGateway;
private $ordersGateway;
private $usersGateway;
private $resourcesGateway;
private $configGateway;
public function __constructor($restAPIClient, $db, $logger, // ...6 more, each for the private vars)
{
$this->restAPIClient = $restAPIClient;
$this->db= $db;
$this->logger= $logger;
// ... 6 more
}
// this method uses all 9 dependencies from the constructor
public function createPaypalPayment()
{
// insert purchase info into `payment` db table. Requires $paymentGateway and $db
// make paypal API call. Requires $restAPIClient, $db, $paypalGateway, $logger
// based on API call results, update `orders`, `users`, `payment`, `resources`, and `config` db tables. Requires $db, $logger, $paymentGateway, $ordersGateway, $usersGateway, $resourcesGateway, $configGateway
}
// this method only requires 1 of the dependencies, $paypalGateway
public function paypalApiEndpoint()
{
// get a value from our db table `paypal`, and return it. Requires $paypalGateway
}
}
In many places throughout my codebase I only need to use the paypalApiEndpoint method. The problem is, in order to use that method I have to first create 9 objects (the dependencies of class Paypal), then I can use those to create the Paypal object, and then finally I can use the simple paypalApiEndpoint method. Needing to create 10 objects in order to this very simple method seems overboard.
Is there a better way?
// this method only requires 1 of the dependencies, $paypalGateway
public function paypalApiEndpoint()
{
// get a value from our db table `paypal`, and return it. Requires $paypalGateway
}
If the only requirement is the paypalGateway, then move the implementation to the paypalGateway class or an extension.
That doesn't mean you would have to remove the method from the Paypal class:
public function paypalApiEndpoint()
{
return $this->paypalGateway->paypalApiEndpoint();
}
Further, your constructor has lots of parameters.
I believe it would be better to have a Builder class with all the dependencies, using setters, that would be injected as an argument:
https://jlordiales.me/2012/12/13/the-builder-pattern-in-practice/
Or implement composites of related data reducing the parameters number:
https://refactoring.guru/smells/data-clumps
Taking into account that the Paypal constructor has lots of parameters:
https://refactoring.guru/smells/long-parameter-list
If there are several unrelated data elements, sometimes you can merge
them into a single parameter object via Introduce Parameter Object.
https://refactoring.guru/introduce-parameter-object
By consolidating parameters in a single class, you can also move the
methods for handling this data there as well, freeing the other
methods from this code.
(...) see whether there is any point in moving a part of the method
(or sometimes even the whole method) to a parameter object class. If
so, use Move Method or Extract Method.
This means that if you only need to invoke paypalApiEndpoint, you may use only the paypalGateway class for your purposes, with less dependancies than the Paypal class.
You don't have to inject all your dependencies in the constructor. You can have setter injectors for optional parameters.
public function setRestAPIClient($restAPIClient) {
$this->restAPIClient = $restAPIClient;
};
In your example, paypalApiEndpoint is a good candidate to be turned into either a class constant and/or a static method that would then allow you to use it via:
class PayPal {
private static $paypalGateway = null;
public static setPaypalGateway($paypalGateway) {
self::paypalGateway = $paypalGateway;
}
public __construct(.... all params) {
self::paypalGateway = $paypalGateway;
$this->otherDependency = $otherDependency;
}
}
For static use you just have to call the static setter 1 time, then use as much as you want within script execution.
Paypal::setPaypalGateway($ppgateway);
$endpoint = Paypal::paypalApiEndpoint();
Last but not least, you might want to look into use of a DI Container Component.
Some DIC's now feature autowiring which can determine what they need to load at calltime using typehints.
There is a standard PSR standard for how DI Containers should work and Symfony2's DIC, Laravel's DIC and PHP-DI for example, all conform to those standards and can easily be used to handle class loading within your application.
As mentioned, PHP-DI is another DIC component you might want to look at as well.

Can I create a parameter from a service in Symfony

I need to set a parameter (an integer) in the service container using a service in much the same way that I can create a service using another service as defined in the service config by defining the factory class and method.
The only way I can think of to do this is to wrap the value in a class and create that class as a service using the method above which seems a bit awkward to me.
Any ideas?
EDIT
To clarify. There is an integer value that I need to inject into a number of different services. This value is calculated by a service.
If it were an object then I could define it as a service, and create it using the factory class and definition parameters and then inject this service into the other services where it is required.
Is it possible to do this for an integer value or will I have to wrap it in a Value Object / Class in order to do this?
A better solution than injecting the result of a service calculation is injecting the service itself:
class ProviderService {
public function calculateResult() {
return 42; // your integer result
}
}
class ConsumerService {
private $provider;
public function __construct(ProviderService $provider) {
$this->provider = $provider;
}
public function execute() {
$result = $this->provider->calulateResult();
// business logic
}
}
If the calulation of your integer value is expensive, you can cache it in the ProviderService.
As simple as it may sound, a value cannot be a service. So you cannot inject a value derived from a service. If you keep your configuration as-is, you need to define a value object or you need to inject the "factory" service to the service which need to calculate the value.
If the value ONLY depends from the configuration, you can add a compiler pass which calculate the value once and set it to all services which need it. During a compiler pass, you can alter the arguments for a Service constructor or add a call to a setter method.
If the value need to be determined at runtime, then it make sense to create a new service. That's because there should be a service which "detect" the change and "notify" all the dependant service.

Better way to populate entity from request?

I'm working with a Symfony 2.1 application and I have a lot of parameters being sent via a POST request and I'm looking for a smarter way to take each request parameter and populate the my entity class. I'm looking to avoid writing $entity->setMyParam($my_param) expressions for n request parameters. For example, here is a snippet of my entity:
namespace Brea\ApiBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Brea\ApiBundle\Entity\Distributions
*
* #ORM\Table(name="distributions")
* #ORM\Entity
*/
class Distributions
{
/**
* #var string $recordType
*
* #ORM\Column(name="record_type", type="string", nullable=false)
* #Assert\NotBlank()
* #Assert\Choice(choices = {"a", "b", "c", "d", "e"}, message = "Choose a valid record type")
*/
private $recordType;
/**
* Set recordType
*
* #param string $recordType
*/
public function setRecordType($recordType)
{
$this->recordType = $recordType;
}
/**
* Get recordType
*
* #return string
*/
public function getRecordType()
{
return $this->recordType;
}
}
My controller attempts to take each request, camelcase the parameters and set the value of the request param to the entity:
public function createRecordAction(Request $request, $id)
{
$distribution = new Distributions();
$params = $request->request;
foreach ($request->request->all() as $param=>$value)
{
if ($param == "_method")
continue;
$function = "set".str_replace(' ','',ucwords(preg_replace('/[^A-Z^a-z^0-9]+/',' ',$param)));
$distribution->$function($value);
}
}
It works, but my misgiving about this approach is that I would need to run this code in every controller that does a similar thing. I can refactor it into a parent class as a method to avoid duplicating code, but I'm curious if this is a good practice. I looked for something in the Symfony framework that does this already but all I could find were examples of binding the request to a form.
First of all: Warning!!
As I commented earlier, I would be very careful about using the code provided in your original post since you said it's data from a POST request, which means a client could inject any kind of data in it and call functions you might not have wanted on your object (or just cause a failure in your script by sending you non-existant function names).
I would actually read the conclusion first..! :) Then come back to Alt. 1 & 2.
Alternative 1:
With that being said, an alternative solution to your problem would be to give objects the responsability to fetch their own data. With granulated enough objects, you should not end up with bloated code, and you can define in each class which parameters to look for and which functions to call (and localize changes when you make a change to a class):
class BookInformation{
private $publisher;
private $name;
private $price;
public static createFromRequest($req){
$publisher = Publisher::createFromRequest($req);
$book = new BookInformation($publisher, $req['book_name'], $req['book_price']);
$book->setABC($req['abc']);
//...
return $book;
}
public __construct($publisher, $name, $price){
//...
}
}
class Publisher{
private $name;
private $address;
public static createFromRequest($req){
return new Publisher($req['publisher_name'], $req['publisher_address']);
}
public __construct($name, $address){
//...
}
}
Like I said before, one big advantage with this method is that if you need to add new attributes to any of those classes, you don't have to edit the controllers at all and can just edit your "initialization from request method". Future changes will be localized to the modified class.
Of course, don't forget to validate any data sent from a user request (but that's just common sense).
Alternative 2:
Note that the first alternative is very similar to the Factory pattern (based on GoF's Abstract Factory), and that you could also implement a solution using that pattern:
class BookFactory{
public createBookInformation($req){
$publisher = $this->createPublisher($req);
$book = new BookInformation($publisher, $req['book_name'], $req['book_price']);
$book->setABC($req['abc']);
//...
return $book;
}
public createPublisher($req){
return new Publisher($req['publisher_name'], $req['publisher_address']);
}
//createAnythingRelatedToBooks($req)...
}
That way, you have all the initializing procedures in a very cohesive class which only responsability is to initiliaze a certain family of objects based on a request object (and that is a VERY good thing). However, if you add attributes to one of those classes, you have to edit the appropriate Factory method too.
Conclusion
Please note that these two alternatives are actually not really alternatives... They can be used along with your initial code (especially the Factory one). They really only solve your last problem (the "where to put the code" problem).
However, even if you do sanitize the POST request and only call functions that are annoted (as stated earlier), I wouldn't really suggest it because I have the feeling that more complex business rules would ruin the design pretty fast (but maybe you've got it all covered already (?)). That is, I don't think you can easily plug in business rules in the initializing process since it's all automatic (it can't do any validation of the values since it could be any kind of value) and I feel like you'd end up "undo-ing" stuff after initialization (which I personally hate.. Lots of room for errors)!
For example, take the same two classes in Alternative 1 (BookInformation and Publisher).
Let's say a Book can only have a Publisher if that Publisher is already registered in a database and that their address has been confirmed (new publishers need to be created using another interface and then have their address confirmed before they can be linked to a book).
Else, regardless of the request data, publisher should be set to XYZ. I have the feeling (I might be wrong) that to support those kind of rules, you'd have to actually construct the object (automatically) and then destroy/reassign the publisher attribute if it does not match certain rules. Now, if you have a pool of those Publisher objects in memory, you also need to remember to delete the wrongly created Publisher in that pool. And that's just one rule!
One thing you could do with your code to "fix" that issue would be to have a validation method for each setter (validXYZ()), but that's starting to look like a design that would fall apart pretty quickly if validations are dependent on other objects/data...
I don't really have anything else to discourage you from using that code, but if you do, please keep us updated on how it works out after a year or two (once there has been some maintenance/new features added, etc...).
I looked for something in the Symfony framework that does this already but all I could find were examples of binding the request to a form.
I would use Forms for this. Even if the HTTP request is not performed from an HTMl form, you can just bind the Request to a form instance: it will take care of all the data injection and validation.
And plus, in case you'll ever need HTML forms, you'll have them ready ^^.

Is it a better practice to inject all variables in the constructor or to use setters and throw exceptions if they are not set?

Imagine you have this class
class Ai1ec_Less_Parser_Controller {
/**
* #var Ai1ec_Read_Variables_Startegy
*/
private $read_variable_strategy;
/**
* #var Ai1ec_Save_Variables_Strategy
*/
private $write_variable_strategy;
/**
* #var Ai1ec_Less_Variables_Collection
*/
private $less_variables_collection;
/**
* #var Ai1ec_Less_Parser
*/
private $ai1ec_less_parser;
/**
* We set the private variables in the constructor. I feel that there are too many parameters.
* Should i use setter instead and throw an exception if something is not set?
*
* #param Ai1ec_Read_Variables_Startegy $read_variable_strategy
* #param Ai1ec_Save_Variables_Strategy $write_variable_strategy
* #param Ai1ec_Less_Variables_Collection $less_variables_collection
* #param Ai1ec_Less_Parser $ai1ec_less_parser
*/
public function __construct( Ai1ec_Read_Variables_Startegy $read_variable_strategy,
Ai1ec_Save_Variables_Strategy $write_variable_strategy,
Ai1ec_Less_Variables_Collection $less_variables_collection,
Ai1ec_Less_Parser $ai1ec_less_parser ) {
}
}
I need those variables to be set and so i set them in the constructor ( but that look like too many parameters ). Another option would be to use setters to set them and then in a method throw an exception if one of the required variables is not set like this
public function do_something_with_parser_and_read_strategy() {
if( $this->are_paser_and_read_strategy_set === false ) {
throw new Exception( "You must set them!" );
}
}
private function are_paser_and_read_strategy_set () {
return isset( $this->read_variable_strategy ) && isset( $this->ai1ec_less_parser );
}
Do you think that one of the two methods is better?And why?
Is your class immutable? If so, then having 100% member population via the constructor is often the best way to do it, but I'll agree it can start to look ugly if you have more than a 5 or 6 parameters.
If your class is mutable then there's no benefit from having a constructor with required parameters. Expose the members via accessor/mutator methods (aka properties).
The factory pattern (as suggested by #Ray) can help, but only if you have a variety of similar classes - for a one-off then you can simply use static methods to instantiate the object, but you'll still have the "too many parameters" problem.
The final alternative is to accept an object with fields (one field for each parameter), but use this technique carefully - if some values are optional then just use method overloading (which unfortunately PHP doesn't support).
I'd just stick with what you're doing and only change it for something else if it presents a problem.
Class naming Controller somehow reflects MVC, or in general - any mechanism responsible for processing sequence.
Data object classes tend to have many fields in any case - it is their responsibility.
Regular object relying on many other objects could be possibly missing a point.
There are four objects, as I see: read, save, parse and provide collection interface to something.
Why shall one have different interfaces for reading and writing? Could this not be combined into one?
Parser shall be a library on itself, thus there may be no reason to combine it anywhere, although it could possible use readers/writers for itself, and, in return, provide collection. Thus could it be possible that parser would take an argument of reader and return a collection object?
That is more about specific case.
In general - having many arguments to the method (or initializing many fields within an object by other objects of different domains) indicates some sort of design flaw.
Kind of on-topic might be this article on Constructor Initialization - it advises to use in-constructor initialization. Just be sure to follow up to the point:
What if there's a lot of collaborators to provide in the constructor? A large list of construction parameters, like any large
parameter list, is a CodeSmell.
And as Ray has written - there is a possibility to initialize using setters, and there is article on that too. To the extent of my view - I think that Martin Fowler really summarizes these cases pretty well.
There is no "better" way. But here are few things you have to consider:
constructors are not inherited
if class requires too many objects, it is responsible for too much
This might have impact on your choice of what sort of interface your class implements.
The general rule of thumb would be this:
If parameters are mandatory for class to function, they should be injected through constructor.
The exception would be, if you initialize the instance by using a factory. It is quite common for factory to build instance form diverse classes, where some of them implement same interface and/or extend same parent class. Then it is easier to inject shared objects through setters.
Creating your objects using factories that call setters instead of using a constuctor of a set number of parameters is much more flexible. Check out the builder and factory patterns.
Throwing exceptions for accessing not fully built objects is good!
Any function that has over 2 (sometimes 3) arguments, I always pass an array, so it would look like:
public function __construct(array $options = array()) {
// Figure out which ones you truly need
if ((!isset($options['arg1'])) || (mb_strlen($options['arg1']) < 1)) {
throw new Exception(sprintf('Invalid $options[arg1]: %s', serialize($options)));
}
// Optional would look like
$this->member2 = (isset($options['arg1'])) && ((int) $options['arg2'] > 0)) ? $options['arg2'] : null;
// Localize required params (already validated above)
$this->member1 = $options['arg1'];
}
Passing an array of options allows for future growth without having to change the function signature. However it does have it's drawback in that the function must localize all elements of the array to ensure access doesn't throw warnings / errors (if an element is missing from the array).
The factory solution is in this case is not a good choice, because you are still left with the problem of passing the values to the factory so it can initialize the object with the correct values.
The standard solution to "too many constructor arguments" is the builder pattern. Your controller class itself will still have a long constructor, but clients can use setters on the builder, which in turn will later call the long constructor.
If you only construct your controller object in one or two places, it wouldn't even be worth all the trouble to create a builder; in that case, just stick with your current code.

Categories