Search code examples
phprestsymfonyapi-platform.com

Symfony Api Platform: Ordering results by custom entity method


In my Symfony Api platform project, I have an entity Community with a custom method getTotalMembers() that returns the number of members in the community. However, when I try to order my results, it doesn't work.

#[ApiFilter(OrderFilter::class, 
      properties: ['id', 'name', 'totalMembers'], 
      arguments: ['orderParameterName' => 'order']
)]

/**
 * @return int current number of users that are members of this community
 */
#[Groups(['community:read','community:read_list'])]
public function getTotalMembers(): ?int
{
    return $this->members->count();
}

Is there a simple way to make it work?


Solution

  • After a lot of struggle I was able to solve partially the issue with custom State processor on the many to many join relationship table. It calls method that adds or removes 1 from regular field that I can use for sorting.

    <?php
    
    namespace App\State;
    
    use ApiPlatform\Metadata\DeleteOperationInterface;
    use App\Entity\Membership;
    use ApiPlatform\Metadata\Operation;
    use ApiPlatform\State\ProcessorInterface;
    use Doctrine\ORM\EntityManagerInterface;
    use Symfony\Component\DependencyInjection\Attribute\Autowire;
    
    final class MembershipStateProcessor implements ProcessorInterface
    {
        public function __construct(
            #[Autowire(service: 'api_platform.doctrine.orm.state.persist_processor')]
            private ProcessorInterface $persistProcessor,
            #[Autowire(service: 'api_platform.doctrine.orm.state.remove_processor')]
            private ProcessorInterface $removeProcessor,
            private EntityManagerInterface $entityManager,
        ) {
        }
        /**
         * @param Membership $data
         */
        public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): Membership|null
        {
            // decrease amount of members inside communities
            if ($operation instanceof DeleteOperationInterface) {
                $subreddit = $data->getSubreddit();
                $subreddit->setTotalMembers(isDelete: true);
                $this->entityManager->persist($subreddit);
                $this->removeProcessor->process($data, $operation, $uriVariables, $context);
                return null;
            }
    
            // increase amount of members inside communities
            $subreddit = $data->getSubreddit();
            $subreddit->setTotalMembers(isDelete: false);
            $this->entityManager->persist($subreddit);
            return $this->persistProcessor->process($data, $operation, $uriVariables, $context);
        }
    }
    
    

    The problem now, however, is that factories do not trigger this class and so entities made with factories aren't counted in, which hinders my ability to test this future.