-1

I have a User object which has a a profilePicturePresignedUrl property. The value of the profilePicturePresignedUrl property needs to be computed using a service.

It would seem that the most efficient way of doing this would be to create a method in the User entity class that would inject the required service like so:

Entity/User.php

public function __construct(FileDownloader $fileDownloader) {
    $this->fileDownloader = $fileDownloader;    
}

public function getProfilePicturePresignedUrl(): string
{
    return $this->fileDownloader->generatePresignedUrl($this->profilePicturePath);
}

However, I have read that it is considered bad practice to inject services in entity classes and can find no references in the documentation of how to do so.

The alternative then would be to generate this value in the controller:

Controller/UserController.php

public function getUserInfo(FileDownloader $fileDownloader)
{
    $user = $this->getUser();
    
    $user->setProfilePicturePresignedUrl($fileDownloader->generatePresignedUrl($user->getProfilePicturePath()));

    return $this->createResponse($user);
}

However, there are many controller methods which call getUser, and it does not seem correct to duplicate this logic in all of them... Is this a situation where it makes sense to inject the service required to generate the presigned URL in the entity class or is there alternative, better approach?

user1392897
  • 831
  • 2
  • 9
  • 25
  • The reason that injecting services into Doctrine entities is discouraged is because there is really no practical way of doing so. Why can't you generate and set the url when the profilePicturePath is assigned? – Cerad Jan 09 '21 at 18:03
  • @Cerad due to the fact that presigned Urls expire (https://docs.aws.amazon.com/AmazonS3/latest/dev/ShareObjectPreSignedURL.html) – user1392897 Jan 10 '21 at 05:22
  • 1
    I see. The postLoad answer which you accepted will certainly work. I would still probably do something during the output phase. Maybe a twig function or something. But if it works then great. – Cerad Jan 10 '21 at 15:13

1 Answers1

1

Doctrine Entity Listeners

Entity listeners are defined as PHP classes that listen to a single Doctrine event on a single entity class. For example, suppose that you want to prepare a specific entity whenever it is loaded from the database. To do so, define a listener for the postLoad Doctrine event. See documentation.

This way, in your case, everytime you fetch a User entity from the database, the property will be initialized and ready to use everywhere in your code. Note that i do not recommand using this for heavy tasks because it will significantly reduce your app performance.

src/EventListener/UserListener.php

namespace App\EventListener;

use App\Entity\User;
use App\Service\FileDownloader;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\Event\LifecycleEventArgs;

class UserListener
{
  private $entityManager;
  private $fileDownloader;

  public function __construct(EntityManagerInterface $entityManager, FileDownloader $fileDownloader)
  {
    $this->entityManager = $entityManager;
    $this->fileDownloader = $fileDownloader; 
  }

  public function postLoad(User $user, LifecycleEventArgs $event): void
  {
    $path = $user->getProfilePicturePath();
    $url = $this->fileDownloader->generatePresignedUrl($path);
    $user->setProfilePicturePresignedUrl($url);
  }
}

config/services.yaml

services:
  App\EventListener\UserListener:
    tags:
      - {
          name: doctrine.orm.entity_listener,
          entity: 'App\Entity\User',
          event: "postLoad"
        }
Reqven
  • 1,688
  • 1
  • 8
  • 13