api-platform / api-platform

🕸️ Create REST and GraphQL APIs, scaffold Jamstack webapps, stream changes in real-time.
https://api-platform.com
MIT License
8.71k stars 963 forks source link

Inject a service into an entity #435

Closed carlespibernat closed 3 years ago

carlespibernat commented 7 years ago

Hello!

I know it's not a good practice but I can't find a solution for my problem:

I have an entity which has a profile photo attribute. This photo is uploaded using VichUploaderBundle which provides a service to retrieve the path of the photo.

So in my entity I have a method to retrieve the path of the photo, but I can't use the service from there, I would need to inject into the entity.

So how can I provide the path of the profile photo of my entities from the api?

Thanks in advance! Carles

P.S. I dont't know if this is the best channel to ask questions, maybe should I ask it in another channel like stackoverflow?

mysiar commented 7 years ago

Create action to do the job instead what you described above Check this out https://github.com/mysiar/api-platform-demo-app/blob/master/src/Mysiar/FileBundle/Action/ReadItem.php

Simperfit commented 7 years ago

I guess an event subscriber will do the job too and you don't need to have a custom endpoint for this. @carlespibernat

carlespibernat commented 7 years ago

Thanks for your answers!

I think I did not express myself well. I have a band entity which response is something like:

{
  "@context": "/contexts/Band",
  "@id": "/bands",
  "@type": "hydra:Collection",
  "hydra:member": [
    {
      "@id": "/bands/1",
      "@type": "Band",
      "id": 1,
      "name": "My band",
      "description": "This is a test band",
      "styles": [
        {
          "@id": "/styles/1",
          "@type": "Style",
          "id": 1,
          "name": "Test style"
        },
        {
          "@id": "/styles/2",
          "@type": "Style",
          "id": 2,
          "name": "Test style 2"
        }
      ]
    }
  ],
  "hydra:totalItems": 1
}

Now I want to add another property to the response with the profile photo, so the response would be like this:

{
  "@context": "/contexts/Band",
  "@id": "/bands",
  "@type": "hydra:Collection",
  "hydra:member": [
    {
      "@id": "/bands/1",
      "@type": "Band",
      "id": 1,
      "name": "My band",
      "description": "This is a test band",
      "styles": [
        {
          "@id": "/styles/1",
          "@type": "Style",
          "id": 1,
          "name": "Test style"
        },
        {
          "@id": "/styles/2",
          "@type": "Style",
          "id": 2,
          "name": "Test style 2"
        }
      ],
      "profilePhoto": "https://domain.com/photos/band.png"
    }
  ],
  "hydra:totalItems": 1
}

The profile url is given by a service, so I need to use this service to get the url.

What I understand from @mysiar answer is to create get a new endpoint where i can retrieve the profile photo path, and what I understand from @Simperfit is to do some action to get the url after another action is done, isn't it?

DariuszLuber commented 7 years ago

I do that by a doctrine event listener

From my code + comments for you:

class DrugListener
{
    public function postLoad(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();//here you have access to your entity
        if ($entity instanceof Drug) {

            $em = $args->getEntityManager();
             //additional operations to get needed data
             $drugDeliveryRepository = $em->getRepository(DrugDelivery::class);

             $storageQuantity = $drugDeliveryRepository->getStorageQuantity( $entity ) ;
             $entity->setStorageQuantity( ($storageQuantity)?$storageQuantity:0 );

             $deliveryCost = $drugDeliveryRepository->getDeliveryCost( $entity ) ;
             $entity->setDeliveryCost( ($deliveryCost)?$deliveryCost:0 );

            $saleData = $em->getRepository(DrugSale::class)->getSoldData( $entity ) ;
            //set additional data
            $entity->setSaleQuantity( ($saleData['quantity'])?$saleData['quantity']:0 );
            $entity->setSaleValue( ($saleData['soldValue'])?$saleData['soldValue']:0 );
            $entity->setSaleProfit( $saleData['soldValue'] - $deliveryCost );

        }
    }

}

and register it

 drug_entity.event_listener:
           class: AppBundle\EventListener\Entity\DrugListener
           tags:
               - { name: doctrine.event_listener, event: postLoad }

Maybe not the best method, couse you can't use those fields to filter. Of cource I had to add additional attributes, getters and setter in entity, but not connected with ORM.

Simperfit commented 7 years ago

@carlespibernat Yeah that's totally my point, you let ApiPlatform do the serialization of the entity then you listen on that tranformation (object to json) to add the little parameters you need. But with the json you provided I understand what you need better, so I guess you should do that in a custom normalizer.

Would you like me to provide an exemple on how to do it ?

carlespibernat commented 7 years ago

Hi!

Thanks for your answers and sorry for the delay.

@DariuszLuber, I can't see how can I get a custom service inside the doctrine event listener :( @Simperfit, could you provide an example? I have tried with the documentation but for some reason I can't get my custom normalizer working.

Thanks! Carles

alanpoulain commented 3 years ago

Greetings! We appreciate your concern but we weren't able to reproduce this issue or it is more of a question. As described in the API Platform contributing guide, we use GitHub issues for bugs and feature requests only.

For support question ("How To", usage advice, or troubleshooting your own code), you have several options:

Feel free to reach one of the support channels above. In the meantime, we are closing this issue.