Resources are not supported in serialized data - symfony

Hello everyone m working on a project there is a bundle CustomerBundle when i call a function following errors are display can anyone tell me why this error coccured..
message": "Resources are not supported in serialized data. Path:
Monolog\Handler\StreamHandler -> Symfony\Bridge\Monolog\Logger ->
Symfony\Component\Cache\Adapter\FilesystemAdapter ->
Symfony\Component\Cache\Adapter\TraceableAdapter ->
Symfony\Component\Cache\DoctrineProvider ->
Doctrine\Common\Annotations\CachedReader ->
Doctrine\ORM\Mapping\Driver\AnnotationDriver ->
Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain ->
Doctrine\ORM\Configuration -> Doctrine\ORM\EntityManager ->
CustomerBundle\Repository\CustomerRepository",
"class": "JMS\Serializer\Exception\RuntimeException",
this is my Customer entity page
/**
* #ORM\ManyToOne(targetEntity="CompanyBundle\Entity\Company", inversedBy="company_customer")
* #ORM\JoinColumn(name="company_id", referencedColumnName="id")
*/
private $companyId;
/**
* #var \DateTime
*
* #ORM\Column(name="created_time", type="datetimetz")
*/
private $createdTime;
/**
* #var \DateTime
*
* #ORM\Column(name="modified_time", type="datetime")
*/
private $modifiedTime;
/**
* #ORM\OneToMany(targetEntity="SalesBundle\Entity\SalesAccount", mappedBy="customerId" )
*/
private $sales_customer_id;
CustomerApiController controller
namespace CustomerBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Controller\FOSRestController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use FOS\RestBundle\View\View;
use CustomerBundle\Entity\Customer;
class CustomerapiController extends FOSRestController
{
/**
* #Rest\Post("/api/customer")
*/
public function customerAction(Request $request)
{
$data = $this->getDoctrine()->getRepository(Customer::class);
$data->find(1);
return $data;
}
}

I think you should have a look at this post. Similar problem can be solved in the same way.
Write a custom repository function and do the following instead of your "find(1)" :
public function findFirstElement()
{
$qb = $this->createQueryBuilder('e');
$qb->where('e.id = 1');
return $qb->getQuery()->getArrayResult();
}

Related

symfony / doctrine deliveres partially incorrect data - using uuids

I have a big problem with Symfony, Doctrine and partially incorrect data.
First of all
i use uuids for primary keys
i use jwts for authentication
i use symfony 5.4.x and doctrine-bundle 2.7.x
i use class inheriance for getting my pk on each entity
i donĀ“t use at all relations, because i want to have some flexibility to slice components into their origin e.g. security component as a microservice (but i think this is not really neccesarry on my question)
Sample of JWT content
{
"iat": xxx,
"exp": xxx,
"roles": [
"MY_ROLE"
],
"id": "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE",
"tenant": {
"id": "11111111-2222-3333-4444-555555555555",
"name": "MyTenant"
},
"user": {
"id": "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE",
"username": "MyUser",
"firstname": "My",
"name": "User"
}
}
Sample of Entity: AbstractEntity.php
<?php
namespace App\Entity;
use JsonSerializable;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Uuid;
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
/**
* Class AbstractEntity
* #package App\Entity
*
* #ORM\MappedSuperclass()
*/
abstract class AbstractEntity implements EntityInterface, JsonSerializable
{
/**
* #ORM\Id
* #ORM\Column(type="uuid", unique=true)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class=UuidGenerator::class)
*/
private ?Uuid $id = null;
/**
* #return Uuid|null
*/
public function getId(): ?Uuid
{
return $this->id;
}
/**
* #return bool
*/
public function isNew(): bool
{
return !isset($this->id);
}
/**
* #return array
*/
public function jsonSerialize(): array
{
return [
'id' => $this->getId()->toRfc4122()
];
}
/**
* #return Uuid
*/
public function __toString(): string
{
return $this->getId()->toRfc4122();
}
}
Sample of Entity: Settings.php
<?php
namespace App\Entity\Configuration;
use DateTime;
use App\Entity\AbstractEntity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Uuid;
/**
* #ORM\Entity(repositoryClass="App\Repository\Configuration\SettingsRepository")
* #ORM\Table(name="configuration_settings", indexes={#ORM\Index(columns={"tenant"})})
*/
class Settings extends AbstractEntity
{
/**
* #var Uuid $tenant
*
* #ORM\Column(type="uuid")
*/
private Uuid $tenant;
/**
* #var string|null $payload
*
* #ORM\Column(type="json", nullable=true)
*/
private ?string $payload = "";
/**
* #return Uuid
*/
public function getTenant(): Uuid
{
return $this->tenant;
}
/**
* #param Uuid $tenant
*/
public function setTenant(Uuid $tenant): void
{
$this->tenant = $tenant;
}
/**
* #return string|null
*/
public function getPayload(): ?string
{
return $this->payload;
}
/**
* #param string|null $payload
*/
public function setPayload(?string $payload): void
{
$this->payload = $payload;
}
}
Sample of Controller: SettingsController.php
<?php
namespace App\Controller\Configuration\API;
use App\Entity\Configuration\Settings;
use App\Entity\Security\Role;
use App\Repository\Configuration\SettingsRepository;
use App\Service\Security\UserService;
use Doctrine\ORM\EntityManagerInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Uid\Uuid;
/**
* Settings API controller.
*
* #Route("/api/configuration/settings", name="api_configuration_settings_")
*/
class SettingsController extends AbstractController
{
/**
* #var SettingsRepository
*/
private SettingsRepository $repository;
/**
* SettingsService constructor.
* #param EntityManagerInterface $entityManager
*/
public function __construct(EntityManagerInterface $entityManager)
{
$this->repository = $entityManager->getRepository(Settings::class);
}
/**
* #Route("/obtain", name="obtain", methods={"GET"})
*/
public function obtain(UserService $userService, TokenStorageInterface $tokenStorageInterface, JWTTokenManagerInterface $jwtManager): JsonResponse
{
$userService->isGranted([
Role::ROLE_TENANT_USER
]);
$token = $jwtManager->decode($tokenStorageInterface->getToken());
$tenantAsRFC4122 = $token['tenant']['id']; // Here we have: 11111111-2222-3333-4444-555555555555
$tenant = Uuid::fromString($tenantAsRFC4122); // Here we have a class of Symfony\Component\Uid\UuidV6;
$settings = $this->repository->findOneBy([
'tenant' => $tenant
]);
return new JsonResponse(
$settings
);
}
}
If i now call the API-URL with the Authorization Header including the Bearer as JWT, i will get the Settings for my tenant 11111111-2222-3333-4444-555555555555. But this works not always ...
I can't describe it exactly, because I can't reproduce it, but on one of the systems with a lot of traffic it happens from time to time that despite the correct bearer, i.e. correct tenant, simply wrong settings for another tenant come back.
Is there a doctrine cache somewhere that might cache queries and assign them to the wrong repsonses at the end of the day?
SELECT payload FROM configuration_settings WHERE tenant = '0x1234567890123456789' // here doctrine automaticly strips it to hex
I am so perplexed, because the SQL behind it looks correct and right, here times as an example shortened.
Hope someone can help me.
Thanks,
S.

Get serialization context groups in controller

I have the problem that depending on user rights, there are different context groups used, and I can't find the place where the context groups are set.
For debugging issues I'm searching an possibility to find out which serialization context group an api call is using. This is my code:
<?php
namespace AppBundle\Controller\Api\Upload;
use AppBundle\Entity\Upload\UploadRepository;
use AppBundle\Entity\Upload\UploadType;
use AppBundle\Entity\Upload\UploadTypeRepository;
use Doctrine\ORM\ORMException;
use GuzzleHttp\Psr7\UploadedFile;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use AppBundle\General\Registry;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Serializer\Normalizer\DataUriNormalizer;
use AppBundle\Entity\Upload\Upload;
use AppBundle\Entity\Application\ApplicationData;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\Serializer\Serializer;
/**
* Class UploadController
*
* #package AppBundle\Controller\Api\Upload
*
* #ApiDoc()
* #ApiResource(attributes={"pagination_enabled"=true})
*/
class UploadController extends Controller
/**
* Get an upload.
*
* #ApiDoc(
* resource=true,
* description="gets an upload",
* )
* #Route(
* name="getUploadSpecial",
* path="/fileuploads/{id}",
* defaults={"_api_resource_class"=Upload::class, "_api_item_operation_name"="getUpload"}
* )
* #Method("GET")
*
* #param Upload $data
*
* #return null|string
*
*/
public function getUploadAction($data)
{
// here I'd like to return the serialization context group
return $data;
}
Is there the possibility to get the serialization context group in the controller?
Well, it seems, the controller isn't the right place to find it.
It is better to dump in
src/Serializer/JsonEncoder.php in function encode, right before the return like this:
/**
* {#inheritdoc}
*/
public function encode($data, $format, array $context = [])
{
dump($data, $context);die;
return $this->jsonEncoder->encode($data, $format, $context);
}

Override #Security annotation inside controller in symfony 4

Today I started upgrading my application from symfony 3 to 4 (and so the related libraries) and I couldn't understand why I couldn't make certain routes work (I had a 401 error but they were supposed to be public routes so no security checks were made there), then I ended up finding this question: #Security annotation on controller class being overridden by action method
A recent comment on the question says that while in a previous version of symfony framework extra bundle, if you put the security annotation on both a class and a method inside that class, the method annotation would override the class annotation, now they stack instead.
This can also be seen (altough it's not very clear since you could already put a #Security annotation on both class and method) on the SensioFramework changelog https://github.com/sensiolabs/SensioFrameworkExtraBundle/blob/master/CHANGELOG.md for version 4.0
allowed using multiple #Security annotations (class and method)
This is a very big change for me since a lot of routes in my application relied on that behavior (which was similar to Symfony 1 where you could set a default security behavior and then a more specific one for each action)
/**
* #Route("my-route")
* #Security("is_granted('IS_AUTHENTICATED_FULLY')")
*/
class MyController extends Controller {
/**
* In Symfony 3.x this would've removed security checks for the route,
* now it checks both the class and the method Security expressions
* #Security(true)
*/
public function myAction(Request $request) {
}
}
Is there some way other than "don't upgrade to symfony 4" or "reorganize your code" (which is my "plan B") to have this behavior back? Something like a configuration option or similar...
I can't seem to find anything about this
I had forgot about this question but I did solve this issue by making my own annotation and EventListener.
Disclaimers:
1) My code uses the Dependency Injection bundle to inject and declare services using annotations
2) I'm sharing the code AS IS, with no warranty it'd work for you too, but i hope you can get the gist of it
I created 2 annotations (#IsGrantedDefault and #SecurityDefault) that work exactly like #IsGranted and #Security (they actually extend the original annotations) except they can be applied only to classes, then i created 2 event listeners, one for each annotation. The event listeners also extend the original event listeners, but they just check if a method already has a Security or IsGranted annotation, in which case they do nothing.
IsGrantedDefault.php
<?php
/*
* #author valepu
*/
namespace App\Project\AppBundle\Annotation;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
/**
* #Annotation
* #Target("CLASS")
*/
class IsGrantedDefault extends IsGranted {
public function getAliasName() {
return 'is_granted_default';
}
public function allowArray() {
return false;
}
}
SecurityDefault.php
<?php
/*
* #author valepu
*/
namespace App\Project\AppBundle\Annotation;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
/**
* #Annotation
* #Target("CLASS")
*/
class SecurityDefault extends Security {
public function getAliasName() {
return 'security_default';
}
public function allowArray() {
return false;
}
}
DefaultListenerTrait.php (Values::DEFAULT_LISTENER_PREFIX is just a string with an underscore "_")
<?php
/*
* #author valepu
*/
namespace App\Project\AppBundle\Event\Traits;
use App\Project\AppBundle\Utils\Values;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent;
Trait DefaultListenerTrait {
/**
* #var string
*/
private $defaultAttribute;
/**
* #var string
*/
private $otherAttributes = [];
/**
* #var string
*/
private $attribute;
/**
* Sets the class attributes
* #param [type] $defaultAnnotation
* #param string|null $modifyAttr
* #return void
*/
protected function setAttributes($defaultAnnotation, ?string $modifyAttr) {
//Get the attirbutes names
$this->attribute = $modifyAttr;
$this->defaultAttribute = Values::DEFAULT_LISTENER_PREFIX . $defaultAnnotation->getAliasName();
$annotations = [new IsGranted([]), new Security([])];
foreach($annotations as $annotation) {
$this->otherAttributes[] = Values::DEFAULT_LISTENER_PREFIX . $annotation->getAliasName();
}
}
/**
* Checks wheter or not the request needs to be handled by the annotation. If it does adds the correct attribute to the request
* #param \Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent $event
* #return boolean
*/
protected function updateDefaultListener(FilterControllerArgumentsEvent $event) {
$request = $event->getRequest();
$default = $request->attributes->get($this->defaultAttribute);
//If there's already an "IsGranted" annotation or there's no "IsGrantedDefault" annotation
if (!$default) {
return false;
}
foreach($this->otherAttributes as $attr) {
if ($request->attributes->get($attr) || !$default) {
return false;
}
}
//We set IsGranted from the default and then call the parent eventListener so that it can handle the security
$request->attributes->set($this->attribute, [$default]);
return true;
}
/**
* Calls the event listener for the class if the request is handled by the class
* #param \Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent $event
* #return void
*/
protected function callEventListener(FilterControllerArgumentsEvent $event) {
if($this->updateDefaultListener($event)) {
parent::onKernelControllerArguments($event);
}
}
}
IsGrantedDefaultListener.php
<?php
/*
* #author valepu
*/
namespace App\Project\AppBundle\Event;
use App\Project\AppBundle\Annotation\IsGrantedDefault;
use App\Project\AppBundle\Event\Traits\DefaultListenerTrait;
use App\Project\AppBundle\Utils\Values;
use RS\DiExtraBundle\Annotation as DI;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Sensio\Bundle\FrameworkExtraBundle\EventListener\IsGrantedListener;
use Sensio\Bundle\FrameworkExtraBundle\Request\ArgumentNameConverter;
use Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
/**
* #DI\Service(autowire = true)
* #DI\Tag("kernel.event_subscriber")
*/
class IsGrantedDefaultListener extends IsGrantedListener {
use DefaultListenerTrait;
/**
* #param \Sensio\Bundle\FrameworkExtraBundle\Request\ArgumentNameConverter $argumentNameConverter
* #param \Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface $authChecker
* #DI\InjectParams({
* "argumentNameConverter" = #DI\Inject("framework_extra_bundle.argument_name_convertor"),
* "authChecker" = #DI\Inject("security.authorization_checker")
* })
*/
public function __construct(ArgumentNameConverter $argumentNameConverter, AuthorizationCheckerInterface $authChecker = null) {
parent::__construct($argumentNameConverter, $authChecker);
$modifyAttr = new IsGranted([]);
$this->setAttributes(new IsGrantedDefault([]), Values::DEFAULT_LISTENER_PREFIX . $modifyAttr->getAliasName());
}
/**
* #param \Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent $event
* #return void
*/
public function onKernelControllerArguments(FilterControllerArgumentsEvent $event) {
$this->callEventListener($event);
}
/**
* {#inheritdoc}
*/
public static function getSubscribedEvents() {
return [KernelEvents::CONTROLLER_ARGUMENTS => 'onKernelControllerArguments'];
}
}
SecurityDefaultListener.php
<?php
/*
* #author valepu
*/
namespace App\Project\AppBundle\Event;
use App\Project\AppBundle\Annotation\SecurityDefault;
use App\Project\AppBundle\Event\Traits\DefaultListenerTrait;
use App\Project\AppBundle\Utils\Values;
use Psr\Log\LoggerInterface;
use RS\DiExtraBundle\Annotation as DI;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Sensio\Bundle\FrameworkExtraBundle\EventListener\SecurityListener;
use Sensio\Bundle\FrameworkExtraBundle\Request\ArgumentNameConverter;
use Sensio\Bundle\FrameworkExtraBundle\Security\ExpressionLanguage;
use Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
/**
* #DI\Service(autowire = true)
* #DI\Tag("kernel.event_subscriber")
*/
class SecurityDefaultListener extends SecurityListener {
use DefaultListenerTrait;
/**
* #param \Sensio\Bundle\FrameworkExtraBundle\Request\ArgumentNameConverter $argumentNameConverter
* #param \Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface $authChecker
* #DI\InjectParams({
* "argumentNameConverter" = #DI\Inject("framework_extra_bundle.argument_name_convertor"),
* "language" = #DI\Inject("sensio_framework_extra.security.expression_language.default"),
* "trustResolver" = #DI\Inject("security.authentication.trust_resolver"),
* "roleHierarchy" = #DI\Inject("security.role_hierarchy"),
* "tokenStorage" = #DI\Inject("security.token_storage"),
* "authChecker" = #DI\Inject("security.authorization_checker"),
* "logger" = #DI\Inject("logger")
* })
*
*/
public function __construct(ArgumentNameConverter $argumentNameConverter, ExpressionLanguage $language = null, AuthenticationTrustResolverInterface $trustResolver = null, RoleHierarchyInterface $roleHierarchy = null, TokenStorageInterface $tokenStorage = null, AuthorizationCheckerInterface $authChecker = null, LoggerInterface $logger = null) {
parent::__construct($argumentNameConverter, $language, $trustResolver, $roleHierarchy, $tokenStorage, $authChecker, $logger);
$modifyAttr = new Security([]);
$this->setAttributes(new SecurityDefault([]), Values::DEFAULT_LISTENER_PREFIX . $modifyAttr->getAliasName());
}
public function onKernelControllerArguments(FilterControllerArgumentsEvent $event) {
$this->callEventListener($event);
}
/**
* {#inheritdoc}
*/
public static function getSubscribedEvents() {
return [KernelEvents::CONTROLLER_ARGUMENTS => 'onKernelControllerArguments'];
}
}
You can delete the class annotation and declare them on all methods

How to access to Doctrine Document Manager from Doctrine Entity Repository

I have a doctrine entity User and a document Address (stored in mongoDB). I want to set an one to many relation between them by userId property. (the user has many addresses)
My User Entity:
namespace BlaBla\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $firstName;
... and so on
My Address document:
namespace BlaBla\UserBundle\Document;
/**
* BlaBla\UserBundle\Document\Address
*/
class Address
{
/**
* #var MongoId $id
*/
protected $id;
/**
* #var string $firstName
*/
protected $firstName;
/**
* #var string $lastName
*/
protected $lastName;
/**
* #var int $userId
*/
protected $userId;
... and so on
My goal is to create the getUser() method for the Address object and the getAddresses() method for the User object.
I've decided to place the method getAddresses() to the doctrine UserRepository class and to inject there the necessary document manager to be able to access to the Address Document. I've overriden the constructor of the userRepository and passed to it the necessary document manager object.
Please, look to the UserRepository class:
<?php
namespace BlaBla\UserBundle\Repository;
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository
{
/**
* #var \Doctrine\ODM\MongoDB\DocumentManager
*/
private $_dm;
/**
* #param \Doctrine\ORM\EntityManager $dm
*/
public function __construct(\Doctrine\ORM\EntityManager $em, $dm) {
$metaData = new \Doctrine\ORM\Mapping\ClassMetadata('BlaBla\UserBundle\Entity\User');
parent::__construct($em, $metaData);
$this->_dm = $dm;
}
/**
* #param $user_id integer
* #return \BlaBla\UserBundle\Document\Address
*/
public function getAddress($user_id) {
$address = $this->_dm->getRepository('BlaBlaUserBundle:Address');
$rt = $address->findByUserId($user_id);
return $rt;
}
public function getAllUsers()
{
return $this->findAll();
}
}
After this I can access to the repository from my controller via:
$em = $this->getDoctrine()->getManager();
$dm = $this->get('doctrine_mongodb')->getManager();
$t = new \BlaBla\UserBundle\Repository\UserRepository($em, $dm);
var_dump($t->getAddress($id));
var_dump($t->getAllUsers());
Both methods work just fine, but now I can't access to the repository using shortcuts like:
$user = $this->getDoctrine()->getRepository('BlaBlaUserBundle:User');
I thought about making the Repository as service with something like this:
user.repository:
class: BlaBla\UserBundle\Repository\UserRepository
arguments: [#doctrine.orm.entity_manager, #doctrine.odm.mongo_db.document_manager]
in my services.yml file, but this only lets me to access the repository with:
$this->get('user.repository');
the default shortcuts doesn't work still.
Please help to find a correct solution for this problem.
Thanks.
Where did you specified the UserRepository? In your User.php with annotation ? Maybe that is the only thing what is missing.
But if you want to use entity and document repository, I advise you to use Doctrine extensions, specifically Reference.

override symfony2 bundle controller actions

/src/Vendor/JobQueueBundle/Controller/DefaultController.php
namespace Vendor\JobQueueBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use JMS\JobQueueBundle\Controller\JobController;
/**
* #Route("/jobs")
*/
class DefaultController extends JobController
{
/**
* #Route("/index")
*/
public function indexAction()
{
die();
}
}
/app/config/routing.yml
vendor_api_job_queue:
resource: "#VendorJobQueueBundle/Controller/"
type: annotation
prefix: /
JMSJobQueueBundle:
resource: "#JMSJobQueueBundle/Controller/"
type: annotation
/src/Syntetik/API/JobQueueBundle/SyntetikAPIJobQueueBundle.php
namespace Vendor\JobQueueBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class VendorJobQueueBundle extends Bundle
{
public function getParent()
{
return 'JMSJobQueueBundle';
}
}
When I try to call /jobs/index I get:
Method "JMS\JobQueueBundle\Controller\JobController::indexAction" does not exist.
DefaultController is completely ignored and not sure why?
Thanks!
It's known and opened issue of JMSDiExtraBundle https://github.com/schmittjoh/JMSDiExtraBundle/issues/39 so problem is that DiExtarBundle doesn't lookup parent class for annotations if child class has not at least one JMS annotation, so proxy class isn't generate at the metadata cache (look app/cache/dev/jms_diextra/metadata/)
The quickest solution is leave at least on annotation:
Parent Controller >>
<?php
namespace Namespace\SiteBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use JMS\DiExtraBundle\Annotation as DI;
class IndexController extends Controller
{
/**
* #DI\Inject("doctrine.orm.entity_manager")
* #var \Doctrine\ORM\EntityManager $em
*/
protected $em;
/**
* #DI\Inject("namespace.search.manager")
* #var Namespace\SearchBundle\Services\SearchManager $searchManager
*/
protected $searchManager;
/**
* #DI\Inject("namespace.product.manager")
* #var Namespace\ProductBundle\Services\ProductManager $productManager
*/
protected $productManager;
/**
* #Route("/", name="homepage")
* #Template()
*/
public function indexAction() {
echo "parent!";
$defaultCategory = $this->searchManager->getDefaultCategory();
....
return $result;
}
}
Child controller >>
<?php
namespace OtherSpace\SiteBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use JMS\DiExtraBundle\Annotation as DI;
use Namespace\SiteBundle\Controller\IndexController as BaseIndexController;
class IndexController extends BaseIndexController
{
/**
* The temprary solution base on known JMS/DiExtraBundle open issue https://github.com/schmittjoh/JMSDiExtraBundle/issues/39
* **We need to leave at leat one Inject in child class to get a proxy generated**
*
* #DI\Inject("doctrine.orm.entity_manager")
* #var \Doctrine\ORM\EntityManager $em
*/
protected $em;
/**
* #Route("/", name="homepage")
* #Template()
*/
public function indexAction() {
echo "child!";
$result = parent::indexAction();
return $result;
}
}
So this way proxy class will be generated for SpaceOther-IndexController and annotations will work
I also worked around fix this issue, you can look to my pull request https://github.com/schmittjoh/JMSDiExtraBundle/pull/153
Figured out the problem with this. It seems it's not symfony2 specific.
The problem is JMS\DiExtraBundle\JMSDiExtraBundle which mess the things around. Just removing that bundle makes everything works by the book.
Thanks!

Resources