ReflectionException with Symfony and Sonata, "Class does not exist" - php

I'm experiencing a problem trying to fit my Entities in the Sonata Admin Bundle. I have something like 5 entities, which are finely listed and viewed in the bundle, but I cannot edit or create a new entry.
When I try to edit or create, I get a ReflectionException error:
Class does not exist
I tried in order to solve that problem to operate on namespaces (moving controller in the same namespace that Admin files, or so) or to tweak the Admin Controller in order to tell it about my entities ("->add('individual', 'entity', array('class' => 'Platform\ProjectBundle\Entity\Individual'))" instead of ->add('individual')).
My entity is named Biosample. Here is the Entity file:
<?php
namespace Platform\ProjectBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="biosample")
*/
class Biosample
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Individual", inversedBy="samples")
* #ORM\JoinColumn(name="individual_id", referencedColumnName="id")
*/
protected $individual;
/**
* #ORM\Column(type="string", length=100)
*/
protected $organ;
/**
* #ORM\Column(type="string", length=100)
*/
protected $sample_name;
/**
* #ORM\Column(type="string", nullable=true)
*/
protected $organ_location;
/**
* #ORM\Column(type="string")
*/
protected $tissue_type;
/**
* #ORM\Column(type="string", nullable=true)
*/
protected $tissue_subtype;
/**
* #ORM\Column(type="datetimetz")
*/
protected $sampling_date;
/**
* #ORM\Column(type="decimal", scale=3, nullable=true)
*/
protected $cellularity;
/**
* #ORM\Column(type="string", nullable=true)
*/
protected $conservation;
/**
* #ORM\Column(type="text", nullable=true)
*/
protected $description;
/**
* #ORM\ManyToMany(targetEntity="Project", mappedBy="biosamples")
*/
protected $projects;
public function __construct()
{
$this->projects = new ArrayCollection();
}
public function __toString()
{
return $this->sample_name;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set organ
*
* #param string $organ
* #return Biosample
*/
public function setOrgan($organ)
{
$this->organ = $organ;
return $this;
}
/**
* Get organ
*
* #return string
*/
public function getOrgan()
{
return $this->organ;
}
/**
* Set organ_location
*
* #param string $organLocation
* #return Biosample
*/
public function setOrganLocation($organLocation)
{
$this->organ_location = $organLocation;
return $this;
}
/**
* Get organ_location
*
* #return string
*/
public function getOrganLocation()
{
return $this->organ_location;
}
/**
* Set tissue_type
*
* #param string $tissueType
* #return Biosample
*/
public function setTissueType($tissueType)
{
$this->tissue_type = $tissueType;
return $this;
}
/**
* Get tissue_type
*
* #return string
*/
public function getTissueType()
{
return $this->tissue_type;
}
/**
* Set tissue_subtype
*
* #param string $tissueSubtype
* #return Biosample
*/
public function setTissueSubtype($tissueSubtype)
{
$this->tissue_subtype = $tissueSubtype;
return $this;
}
/**
* Get tissue_subtype
*
* #return string
*/
public function getTissueSubtype()
{
return $this->tissue_subtype;
}
/**
* Set sampling_date
*
* #param \DateTime $samplingDate
* #return Biosample
*/
public function setSamplingDate($samplingDate)
{
$this->sampling_date = $samplingDate;
return $this;
}
/**
* Get sampling_date
*
* #return \DateTime
*/
public function getSamplingDate()
{
return $this->sampling_date;
}
/**
* Set cellularity
*
* #param string $cellularity
* #return Biosample
*/
public function setCellularity($cellularity)
{
$this->cellularity = $cellularity;
return $this;
}
/**
* Get cellularity
*
* #return string
*/
public function getCellularity()
{
return $this->cellularity;
}
/**
* Set conservation
*
* #param string $conservation
* #return Biosample
*/
public function setConservation($conservation)
{
$this->conservation = $conservation;
return $this;
}
/**
* Get conservation
*
* #return string
*/
public function getConservation()
{
return $this->conservation;
}
/**
* Set description
*
* #param string $description
* #return Biosample
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set individual
*
* #param \Platform\ProjectBundle\Entity\Individual $individual
* #return Biosample
*/
public function setIndividual(\Platform\ProjectBundle\Entity\Individual $individual = null)
{
$this->individual = $individual;
return $this;
}
/**
* Get individual
*
* #return \Platform\ProjectBundle\Entity\Individual
*/
public function getIndividual()
{
return $this->individual;
}
/**
* Add projects
*
* #param \Platform\ProjectBundle\Entity\Project $projects
* #return Biosample
*/
public function addProject(\Platform\ProjectBundle\Entity\Project $projects)
{
$this->projects[] = $projects;
return $this;
}
/**
* Remove projects
*
* #param \Platform\ProjectBundle\Entity\Project $projects
*/
public function removeProject(\Platform\ProjectBundle\Entity\Project $projects)
{
$this->projects->removeElement($projects);
}
/**
* Get projects
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getProjects()
{
return $this->projects;
}
/**
* Set sample_name
*
* #param string $sampleName
* #return Biosample
*/
public function setSampleName($sampleName)
{
$this->sample_name = $sampleName;
return $this;
}
/**
* Get sample_name
*
* #return string
*/
public function getSampleName()
{
return $this->sample_name;
}
}`
Here is my BiosampleAdmin.php:
<?php
namespace Platform\ProjectBundle\Controller\Admin;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use Knp\Menu\ItemInterface as MenuItemInterface;
use Platform\ProjectBundle\Entity\Biosample;
class BiosampleAdmin extends Admin
{
/**
* #param \Sonata\AdminBundle\Show\ShowMapper $showMapper
*
* #return void
*/
protected function configureShowFields(ShowMapper $showMapper)
{
$showMapper
->add('id')
->add('sample_name')
->add('individual', 'entity', array('class' => 'Platform\ProjectBundle\Entity\Individual'))
->add('organ')
->add('organ_location')
->add('tissue_type')
->add('tissue_subtype')
->add('sampling_date')
->add('cellularity')
->add('conservation')
->add('projects', 'entity', array('class' => 'Platform\ProjectBundle\Entity\Project'))
->add('description')
;
}
/**
* #param \Sonata\AdminBundle\Form\FormMapper $formMapper
*
* #return void
*/
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->with('General')
->add('sample_name')
->add('individual')
->add('organ')
->add('organ_location')
->add('tissue_type')
->add('tissue_subtype')
->end()
->with('Miscelaneous')
->add('sampling_date')
->add('cellularity')
->add('conservation')
->end()
->with('Projects')
->add('projects')
->end()
->with('Description')
->add('description', 'sonata_type_model', array('multiple' => false))
->end()
;
}
/**
* #param \Sonata\AdminBundle\Datagrid\ListMapper $listMapper
*
* #return void
*/
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('id')
->add('sample_name')
->add('individual')
->add('projects')
->add('organ')
->add('tissue')
->add('_action', 'actions', array(
'actions' => array(
'show' => array(),
'edit' => array(),
'delete' => array(),
)
))
;
}
/**
* #param \Sonata\AdminBundle\Datagrid\DatagridMapper $datagridMapper
*
* #return void
*/
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper
->add('sample_name')
->add('individual')
->add('projects')
;
}
}
Here is the admin controller:
<?php
namespace Platform\ProjectBundle\Controller;
use Sonata\AdminBundle\Controller\CRUDController as Controller;
use Platform\ProjectBundle\Entity\Biosample;
class BiosampleAdminController extends Controller
{
}
And if you need it, here my composer.json:
{
"name": "symfony/framework-standard-edition",
"license": "MIT",
"type": "project",
"description": "The \"Symfony Standard Edition\" distribution",
"autoload": {
"psr-0": { "": "src/", "SymfonyStandard": "app/" }
},
"require": {
"php": ">=5.3.3",
"symfony/symfony": "2.6.*",
"doctrine/orm": "~2.2,>=2.2.3,<2.5",
"doctrine/dbal": "<2.5",
"doctrine/doctrine-bundle": "~1.2",
"twig/extensions": "~1.0",
"symfony/assetic-bundle": "~2.3",
"symfony/swiftmailer-bundle": "~2.3",
"symfony/monolog-bundle": "~2.4",
"sensio/distribution-bundle": "~3.0,>=3.0.12",
"sensio/framework-extra-bundle": "~3.0,>=3.0.2",
"jms/security-extra-bundle": "~1.2",
"ircmaxell/password-compat": "~1.0.3",
"stof/doctrine-extensions-bundle": "~1.1#dev",
"friendsofsymfony/user-bundle": "~1.3",
"incenteev/composer-parameter-handler": "~2.0",
"nervo/yuicompressor": "2.4.8",
"sonata-project/admin-bundle": "~2.3",
"sonata-project/doctrine-orm-admin-bundle": "~2.3",
"sonata-project/easy-extends-bundle": "~2.1",
"sonata-project/user-bundle": "~2.2",
"knplabs/knp-menu-bundle": "~1.1",
"mopa/bootstrap-bundle": "~2",
"twbs/bootstrap-sass": "~3.3.0",
"knplabs/knp-paginator-bundle": "dev-master",
"knplabs/knp-menu": "~1.1",
"craue/formflow-bundle": "~2.0"
},
"require-dev": {
"sensio/generator-bundle": "~2.3"
},
"scripts": {
"post-root-package-install": [
"SymfonyStandard\\Composer::hookRootPackageInstall"
],
"post-install-cmd": [
"Incenteev\\ParameterHandler\\ScriptHandler::buildParameters",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::removeSymfonyStandardFiles"
],
"post-update-cmd": [
"Incenteev\\ParameterHandler\\ScriptHandler::buildParameters",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::removeSymfonyStandardFiles"
]
},
"config": {
"bin-dir": "bin"
},
"extra": {
"symfony-app-dir": "app",
"symfony-web-dir": "web",
"symfony-assets-install": "relative",
"incenteev-parameters": {
"file": "app/config/parameters.yml"
},
"branch-alias": {
"dev-master": "2.6-dev"
}
}
}
Finally, here's my service declaration and my config.yml file:
Service.yml:
services:
platform.project.admin.biosample:
class: Platform\ProjectBundle\Controller\Admin\BiosampleAdmin
tags:
- { name: sonata.admin, manager_type: orm, group: Project Manager, label: Biosample }
arguments: [null, Platform\ProjectBundle\Entity\Biosample, PlatformProjectBundle:BiosampleAdmin]
Config.yml:
imports:
- { resource: parameters.yml }
- { resource: security.yml }
- { resource: services.yml }
framework:
#esi: ~
translator: { fallbacks: ["%locale%"] }
secret: "%secret%"
router:
resource: "%kernel.root_dir%/config/routing.yml"
strict_requirements: ~
form: ~
csrf_protection: ~
validation: { enable_annotations: true }
templating:
engines: ['twig']
#assets_version: SomeVersionScheme
default_locale: "%locale%"
trusted_hosts: ~
trusted_proxies: ~
session:
# handler_id set to null will use default session handler from php.ini
handler_id: ~
fragments: ~
http_method_override: true
# Twig Configuration
twig:
debug: "%kernel.debug%"
strict_variables: "%kernel.debug%"
# Assetic Configuration
assetic:
debug: "%kernel.debug%"
use_controller: false
bundles: [ ]
#java: /usr/bin/java
filters:
cssrewrite: ~
#closure:
# jar: "%kernel.root_dir%/Resources/java/compiler.jar"
#yui_css:
# jar: "%kernel.root_dir%/Resources/java/yuicompressor-2.4.7.jar"
# Doctrine Configuration
doctrine:
dbal:
driver: "%database_driver%"
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
# if using pdo_sqlite as your database driver:
# 1. add the path in parameters.yml
# e.g. database_path: "%kernel.root_dir%/data/data.db3"
# 2. Uncomment database_path in parameters.yml.dist
# 3. Uncomment next line:
# path: "%database_path%"
types:
json: Sonata\Doctrine\Types\JsonType
orm:
auto_generate_proxy_classes: "%kernel.debug%"
auto_mapping: true
# Swiftmailer Configuration
swiftmailer:
transport: "%mailer_transport%"
host: "%mailer_host%"
username: "%mailer_user%"
password: "%mailer_password%"
spool: { type: memory }
fos_user:
db_driver: orm # other valid values are 'mongodb', 'couchdb' and 'propel'
firewall_name: secured
user_class: Application\Sonata\UserBundle\Entity\User
group:
group_class: Application\Sonata\UserBundle\Entity\Group
group_manager: sonata.user.orm.group_manager
service:
user_manager: sonata.user.orm.user_manager
sonata_doctrine_orm_admin:
entity_manager: ~
sonata_block:
default_contexts: [cms]
blocks:
sonata.admin.block.admin_list:
contexts: [admin]
sonata.user.block.menu: ~
sonata.user.block.account: ~
sonata.block.service.text: ~
sonata_user:
security_acl: true
manager_type: orm
mopa_bootstrap:
form: ~
And last but not least: the full stack trace.
[1] ReflectionException: Class does not exist
at n/a
in /var/www/Project/app/cache/dev/classes.php line 6756
at ReflectionClass->__construct('')
in /var/www/Project/app/cache/dev/classes.php line 6756
at Doctrine\Common\Persistence\AbstractManagerRegistry->getManagerForClass(null)
in /var/www/Project/vendor/sonata-project/doctrine-orm-admin-bundle/Model/ModelManager.php line 220
at Sonata\DoctrineORMAdminBundle\Model\ModelManager->getEntityManager(null)
in /var/www/Project/vendor/sonata-project/doctrine-orm-admin-bundle/Model/ModelManager.php line 54
at Sonata\DoctrineORMAdminBundle\Model\ModelManager->getMetadata(null)
in /var/www/Project/vendor/sonata-project/doctrine-orm-admin-bundle/Model/ModelManager.php line 317
at Sonata\DoctrineORMAdminBundle\Model\ModelManager->getIdentifierFieldNames(null)
in /var/www/Project/app/cache/dev/classes.php line 12663
at Sonata\AdminBundle\Form\ChoiceList\ModelChoiceList->__construct(object(ModelManager), null, null, null, null)
in /var/www/Project/app/cache/dev/classes.php line 13690
at Sonata\AdminBundle\Form\Type\ModelType->Sonata\AdminBundle\Form\Type\{closure}(object(OptionsResolver), object(SimpleChoiceList))
in /var/www/Project/vendor/symfony/symfony/src/Symfony/Component/OptionsResolver/OptionsResolver.php line 836
at Symfony\Component\OptionsResolver\OptionsResolver->offsetGet('choice_list')
in /var/www/Project/vendor/symfony/symfony/src/Symfony/Component/OptionsResolver/OptionsResolver.php line 769
at Symfony\Component\OptionsResolver\OptionsResolver->resolve(array('sonata_field_description' => object(FieldDescription), 'class' => null, 'model_manager' => object(ModelManager), 'multiple' => false, 'label_render' => false, 'label' => 'Description'))
in /var/www/Project/vendor/symfony/symfony/src/Symfony/Component/Form/ResolvedFormType.php line 109
at Symfony\Component\Form\ResolvedFormType->createBuilder(object(FormFactory), 'description', array('sonata_field_description' => object(FieldDescription), 'class' => null, 'model_manager' => object(ModelManager), 'multiple' => false, 'label_render' => false, 'label' => 'Description'))
in /var/www/Project/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/DataCollector/Proxy/ResolvedTypeDataCollectorProxy.php line 82
at Symfony\Component\Form\Extension\DataCollector\Proxy\ResolvedTypeDataCollectorProxy->createBuilder(object(FormFactory), 'description', array('sonata_field_description' => object(FieldDescription), 'class' => null, 'model_manager' => object(ModelManager), 'multiple' => false, 'label_render' => false, 'label' => 'Description'))
in /var/www/Project/vendor/symfony/symfony/src/Symfony/Component/Form/FormFactory.php line 87
at Symfony\Component\Form\FormFactory->createNamedBuilder('description', 'sonata_type_model', null, array('sonata_field_description' => object(FieldDescription), 'class' => null, 'model_manager' => object(ModelManager), 'multiple' => false, 'label_render' => false, 'label' => 'Description'))
in /var/www/Project/vendor/symfony/symfony/src/Symfony/Component/Form/FormBuilder.php line 106
at Symfony\Component\Form\FormBuilder->create('description', 'sonata_type_model', array('sonata_field_description' => object(FieldDescription), 'class' => null, 'model_manager' => object(ModelManager), 'multiple' => false, 'label_render' => false, 'label' => 'Description'))
in /var/www/Project/vendor/symfony/symfony/src/Symfony/Component/Form/FormBuilder.php line 268
at Symfony\Component\Form\FormBuilder->resolveChildren()
in /var/www/Project/vendor/symfony/symfony/src/Symfony/Component/Form/FormBuilder.php line 216
at Symfony\Component\Form\FormBuilder->getForm()
in /var/www/Project/app/cache/dev/classes.php line 9671
at Sonata\AdminBundle\Admin\Admin->buildForm()
in /var/www/Project/app/cache/dev/classes.php line 9930
at Sonata\AdminBundle\Admin\Admin->getForm()
in /var/www/Project/vendor/sonata-project/admin-bundle/Controller/CRUDController.php line 353
at Sonata\AdminBundle\Controller\CRUDController->editAction('1')
in line
at call_user_func_array(array(object(CRUDController), 'editAction'), array('1'))
in /var/www/Project/app/bootstrap.php.cache line 3022
at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), '1')
in /var/www/Project/app/bootstrap.php.cache line 2984
at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), '1', true)
in /var/www/Project/app/bootstrap.php.cache line 3133
at Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel->handle(object(Request), '1', true)
in /var/www/Project/app/bootstrap.php.cache line 2377
at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
in /var/www/Project/web/app_dev.php line 28

Well, the reason for this error was a bad copy/paste from the tutorial.
In the Admin Class, the function configureFormFields contained a field "Description" badly described.
->with('Description')
->add('description', 'sonata_type_model', array('multiple' => false))
->end()
I had to replace it to:
->with('Description')
->add('description')
->end()
I discovered that automatic Admin Class skeleton generation was a function of Sonata Admin Bundle.
In order to automatically generate, execute:
php app/console sonata:admin:generate
Then enter the full path to your entity, in this example:
Platform\ProjectBundle\Entity\Biosample
The admin bundle will parse your entity and :
Generate the Admin Class file
Add an entry in your application bundle's service.yml
Generate optionnaly the CRUD controller
I guess this should be the prefered method when one is starting with sonata admin bundle.

I had the same problem, and for a reason i ignore, not in the past project where i've done exactly the same...
After an hour of search, i realize my table 'fos_user_user_group' has not been created has the mapping is not created. So my solution was simply to override the groups property of my entity User in order to define the Doctrine ORM Mapping :
/**
* The User can have many groups.
* #ORM\ManyToMany(targetEntity="Group")
* #ORM\JoinTable(name="fos_user_user_group",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="group_id", referencedColumnName="id")}
* )
*/
protected $groups;
After that, doctrine:schema:update --force and the problem was solved ;)

If you did it like me, and created first the database then via the console the yml files and from that the classes, then your annotations are useless.
Sonata is using yml and not annotations. They exist and seem to work but they just don't. To make the "Class does not exist" message go check what you yml definitions are saying. I needed to add the relationships and entity classes there.
If this is really the reason for your errors you might want to activate the annotations by deleting or moving the yml or xml templates somewhere else (..\Resources\config\doctrine).

Related

FOSRestBundle integration with BazingaHateoasBundle but links are still missing from response

I'm trying to integrate FOSRestBundle with BazingaHateoasBundle but the links (that I expected to be automatically generated) are still missing from the response. What is wrong with my set-up?
Here are my configs:
composer.json [excerpt]:
"require": {
"php": "^7.1.3",
"friendsofsymfony/rest-bundle": "^2.5",
"jms/serializer-bundle": "^2.4",
"lexik/jwt-authentication-bundle": "^2.6",
"sensio/framework-extra-bundle": "^5.2",
"stof/doctrine-extensions-bundle": "^1.3",
"symfony/flex": "^1.1",
"symfony/framework-bundle": "4.2.*",
"willdurand/hateoas-bundle": "^1.4"
},
jms_serializer.yaml:
jms_serializer:
visitors:
xml:
format_output: '%kernel.debug%'
fos_rest.yaml:
fos_rest:
zone:
- { path: ^/api/* }
view:
view_response_listener: true
format_listener:
rules:
- { path: ^/api, prefer_extension: true, fallback_format: json, priorities: [ json ] }
routing_loader:
default_format: json
body_listener:
array_normalizer: fos_rest.normalizer.camel_keys
serializer:
serialize_null: true
body_converter:
enabled: true
validate: true
And here are my classes:
PersonsController.php:
/**
* #Rest\Route("/persons")
*/
class PersonsController extends AbstractFOSRestController
{
/**
* #Rest\Get("")
* #return View
*/
public function getList()
{
$repository = $this->getDoctrine()->getRepository(Person::class);
$view = $this->view(
$repository->findAll()
);
$view->getContext()->setGroups(['default']);
return $view;
}
}
Person.php
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Hateoas\Configuration\Annotation as Hateoas;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity(repositoryClass="App\Repository\PersonRepository")
* #UniqueEntity("slug")
* #Serializer\ExclusionPolicy("all")
* #Hateoas\Relation("self", href = "expr('/api/persons/' ~ object.getSlug())")
*/
class Person
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
* #Serializer\Expose
* #Serializer\Groups({"default"})
*/
private $name;
/**
* #ORM\Column(type="string", length=255, unique=true)
* #Gedmo\Slug(fields={"name"})
* #Serializer\Expose
* #Serializer\Groups({"default"})
* #Serializer\XmlAttribute
*/
private $slug;
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getSlug()
{
return $this->slug;
}
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
}
Testing:
GET http://localhost:8000/api/groups response:
[
{
"name": "person1",
"slug": "person1"
},
{
"name": "person2",
"slug": "person2"
}
]
Expected:
[
{
"name": "person1",
"slug": "person1",
"_links": {
"self": { "href": "http://example.com/api/persons/person1" }
}
},
{
"name": "person2",
"slug": "person2",
"_links": {
"self": { "href": "http://example.com/api/persons/person2" }
}
}
]
Researched:
https://github.com/willdurand/BazingaHateoasBundle/blob/master/Resources/doc/index.md#serializing-objects - doesn't show integration with the FOS bundle. It just constructs the Response manually.
https://github.com/willdurand/Hateoas/issues/238 - suggested removing serializedGroups, but I need groups to properly hide/expose fields to be displayed
Symfony and Wildurand/Hateoas Bundle - no links on JSON reposnse - the OP just manually constructed the response object, thus did not actually use the features of FOSRestBundle
https://symfony.com/doc/master/bundles/FOSRestBundle/1-setting_up_the_bundle.html#b-enable-a-serializer - Because the JMSSerializerBundle is the only serializer that I installed, I assume that it is the one used by the FOSRestBundle to automatically serialize the data. Thus I should not manually serialize the data and construct the response, contrary to the solutions I found.
I just needed to add the exclusion property to the Relation annotation, and specify the group where it should appear.
/**
* #ORM\Entity(repositoryClass="App\Repository\PersonRepository")
* #UniqueEntity("slug")
* #Serializer\ExclusionPolicy("all")
* #Hateoas\Relation(
* "self",
* href = "expr('/api/persons/' ~ object.getSlug())",
* exclusion = #Hateoas\Exclusion(groups={"default"})
* )
*/
class Person
{ ...

DataFixture aren't saved using nelmio/alice ( sf 3.3 with flex)

Hello I'm using flex to work with Symfony for learning purpose. After I installed a few recipes and I want to add the nelmio/alice for generating fake data for doctrine fixtures but after I load the fixtures no data is saved into mysql. Any ideas what did i do wrong?
<?php
namespace App\DataFixtures\ORM;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Nelmio\Alice\Loader\NativeLoader;
class LoadFixtures extends Fixture
{
/**
* Load data fixtures with the passed EntityManager
*
* #param ObjectManager $manager
*/
public function load(ObjectManager $manager)
{
$loader = new NativeLoader();
$obj = $loader->loadFile(__DIR__ . 'fixtures.yml');
}
fixtures.yml:
App\Entity\BaseUser:
user{1..10}:
email: <email()>
BaseUser entity
<?php
namespace App\Entity;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* Class BaseUser
* #package App\Entity
* #ORM\Entity
* #ORM\Table()
*/
class BaseUser implements AdvancedUserInterface
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", unique=true)
*/
private $email;
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #param mixed $id
* #return BaseUser
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* #return mixed
*/
public function getEmail()
{
return $this->email;
}
/**
* #param mixed $email
* #return BaseUser
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Checks whether the user's account has expired.
*
* Internally, if this method returns false, the authentication system
* will throw an AccountExpiredException and prevent login.
*
* #return bool true if the user's account is non expired, false otherwise
*
* #see AccountExpiredException
*/
public function isAccountNonExpired()
{
// TODO: Implement isAccountNonExpired() method.
}
/**
* Checks whether the user is locked.
*
* Internally, if this method returns false, the authentication system
* will throw a LockedException and prevent login.
*
* #return bool true if the user is not locked, false otherwise
*
* #see LockedException
*/
public function isAccountNonLocked()
{
// TODO: Implement isAccountNonLocked() method.
}
/**
* Checks whether the user's credentials (password) has expired.
*
* Internally, if this method returns false, the authentication system
* will throw a CredentialsExpiredException and prevent login.
*
* #return bool true if the user's credentials are non expired, false otherwise
*
* #see CredentialsExpiredException
*/
public function isCredentialsNonExpired()
{
// TODO: Implement isCredentialsNonExpired() method.
}
/**
* Checks whether the user is enabled.
*
* Internally, if this method returns false, the authentication system
* will throw a DisabledException and prevent login.
*
* #return bool true if the user is enabled, false otherwise
*
* #see DisabledException
*/
public function isEnabled()
{
// TODO: Implement isEnabled() method.
}
/**
* Returns the roles granted to the user.
*
* <code>
* public function getRoles()
* {
* return array('ROLE_USER');
* }
* </code>
*
* Alternatively, the roles might be stored on a ``roles`` property,
* and populated in any number of different ways when the user object
* is created.
*
* #return (Role|string)[] The user roles
*/
public function getRoles()
{
return ['ROLE_USER'];
}
/**
* Returns the password used to authenticate the user.
*
* This should be the encoded password. On authentication, a plain-text
* password will be salted, encoded, and then compared to this value.
*
* #return string The password
*/
public function getPassword()
{
// TODO: Implement getPassword() method.
}
/**
* Returns the salt that was originally used to encode the password.
*
* This can return null if the password was not encoded using a salt.
*
* #return string|null The salt
*/
public function getSalt()
{
// TODO: Implement getSalt() method.
}
/**
* Returns the username used to authenticate the user.
*
* #return string The username
*/
public function getUsername()
{
return $this->email;
}
/**
* Removes sensitive data from the user.
*
* This is important if, at any given point, sensitive information like
* the plain-text password is stored on this object.
*/
public function eraseCredentials()
{
// TODO: Implement eraseCredentials() method.
}
}
bundles.php
<?php
return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class => ['all' => true],
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
Nelmio\Alice\Bridge\Symfony\NelmioAliceBundle::class => ['all' => true],
];
and in frameworky.yml i added this
nelmio_alice:
locale: 'en_US' # Default locale for the Faker Generator
seed: 6399 # Value used make sure Faker generates data consistently across
# runs, set to null to disable.
functions_blacklist: # Some Faker formatter may have the same name as PHP
- 'current' # native functions. PHP functions have the priority,
# so if you want to use a Faker formatter instead,
# blacklist this function here
loading_limit: 5 # Alice may do some recursion to resolve certain values.
# This parameter defines a limit which will stop the
# resolution once reached.
max_unique_values_retry: 150 # Maximum number of time Alice can try to
# generate a unique value before stopping and
# failing.
composer.yml
{
"type": "project",
"license": "proprietary",
"require": {
"php": "^7.0.8",
"doctrine/doctrine-fixtures-bundle": "^2.4",
"doctrine/doctrine-migrations-bundle": "^1.2",
"sensio/framework-extra-bundle": "^5.0",
"sensiolabs/security-checker": "^4.1",
"symfony/console": "^3.3",
"symfony/framework-bundle": "^3.3",
"symfony/orm-pack": "^1.0",
"symfony/security-core": "^3.3",
"symfony/yaml": "^3.3"
},
"require-dev": {
"nelmio/alice": "^3.1",
"symfony/dotenv": "^3.3",
"symfony/flex": "^1.0"
},
"config": {
"preferred-install": {
"*": "dist"
},
"sort-packages": true
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Tests\\": "tests/"
}
},
"scripts": {
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install --symlink --relative %PUBLIC_DIR%": "symfony-cmd",
"security-checker security:check": "script"
},
"post-install-cmd": [
"#auto-scripts"
],
"post-update-cmd": [
"#auto-scripts"
]
},
"conflict": {
"symfony/symfony": "*",
"symfony/twig-bundle": "<3.3",
"symfony/debug": "<3.3"
},
"extra": {
"symfony": {
"id": "01BX9RZX7RBK5CNHP741EVCXB5",
"allow-contrib": false
}
}
}
I was trying to follow the KNP Symfony video tutorial and got lost on Nelmio/Alice repo. Bogdan's answer worked for me.
You need to add Alice Data Fixtures repo.
After adding
Here's a quick guide for all of you who's also lost:
namespace AppBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DataFixtures extends Controller implements FixtureInterface
{
public function load(ObjectManager $manager)
{
//File Path/s needs to be in array
$dummyFilePath =[__DIR__.'/DataFixtures.yml'];
//get fidry loader
$loader = $this->get('fidry_alice_data_fixtures.doctrine.loader');
//execute
$loader->load($dummyFilePath);
}
}
I solved the problem by installing theofidry/alice-data-fixtures since it seems nelmio/alice 3.x doesn't work the same as the older version. ( It doesn't takes care of the database query anymore ) .
LE:
Maybe it's my fault or not but since the official release of the sf 4 i had some problems with the loading part.
Here's a fix for anyone looking into
You need a service like this to load the fixtures
App\DataFixtures\ORM\DataFixtures:
arguments: ['#fidry_alice_data_fixtures.doctrine.purger_loader']
calls:
- [load, ['#doctrine.orm.entity_manager']]
tags: [doctrine.fixture.orm]
And in the method
$files = [
__DIR__ . '/fixtures.yml',
];
$this->loader->load($files);

Vich and gaufrette are not saving files in sonata admin

I'm trying to make an upload linked to an entity in Sonata Admin using Vich.
All the configuration is done but the file does not upload, and I cannot find the error.
The problem is that when y try to upload the file, every thing seem to work fine, Sonata persists the data in all the data base fields, and the file is uploaded to /tmp folder in teh sistem,also, sonata prints the tmp route in the patch field in database. But the file never gets to the folder setted in gaufrette and neither generates the unique name.
Here is the code:
The admin Class:
<?php
namespace DownloadFileAdminBundle\Admin;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;
class DownloadFileAdmin extends Admin
{
const FILE_MAX_SIZE = 2 * 1024 * 1024; // 2 megas
/**
* #param FormMapper $formMapper
*/
protected function configureFormFields(FormMapper $formMapper)
{
$fileOptions = array(
'label' => 'Archivo',
'required' => true,
'vich_file_object' => 'downloadfile',
'vich_file_property' => 'downloadFile',
'vich_allow_delete' => true,
'attr' => array(
'data-max-size' => self::FILE_MAX_SIZE,
'data-max-size-error' => 'El tamaño del archivo no puede ser mayor de 2 megas'
)
);
$formMapper
->add('slug', null, array('label' => 'Slug'))
->add('title', null, array('label' => 'Título'))
->add('description', null, array('label' => 'Descripción'))
->add('roles')
->add('path', 'DownloadFileAdminBundle\Form\Extension\VichFileObjectType', $fileOptions)
;
}
/**
* #param ListMapper $listMapper
*/
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->add('id')
->add('slug')
->add('title')
->add('description')
->add('path')
->add('roles')
->add('_action', null, array(
'actions' => array(
'show' => array(),
'edit' => array(),
'delete' => array(),
)
))
;
}
}
Here is the entity, with the not persistent fieln and the path field, witch is wher eI want tostore the file path:
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
* #Vich\UploadableField(mapping="download_file", fileNameProperty="path")
* #var File
*/
private $downloadFile;
/**
* #ORM\Column(type="string")
*/
protected $path;
public function getDownloadFile()
{
return $this->downloadFile;
}
/**
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile $file
*
* #return File
*/
public function setDownloadFile(File $file = null)
{
$this->downloadFile = $file;
return $this;
}
/**
* #return mixed
*/
public function getPath()
{
return $this->path;
}
/**
* #param mixed $path
*/
public function setPath($path)
{
$this->path = $path;
}
The services os admin.yml
services:
sonata.admin.file:
class: DownloadFileAdminBundle\Admin\DownloadFileAdmin
arguments: [~, Opos\DownloadFileBundle\Entity\DownloadFile, SonataAdminBundle:CRUD]
tags:
- { name: sonata.admin, manager_type: orm, group: "Files", label: "Archivo" }
and services.yml:
services:
download_file_admin_bundle.vich_file_object_type:
class: DownloadFileAdminBundle\Form\Extension\VichFileObjectType
arguments: [ "#doctrine.orm.entity_manager" ]
tags:
- { name: "form.type", alias: "vich_file_object" }
And last vich and gaufrette configuration:
vich_uploader:
db_driver: orm
storage: gaufrette
mappings:
question_image:
uri_prefix: ~
upload_destination: questions_image_fs
namer: vich_uploader.namer_uniqid
download_file:
uri_prefix: ~
upload_destination: download_file_fs
namer: vich_uploader.namer_uniqid
knp_gaufrette:
stream_wrapper: ~
adapters:
questions_image_adapter:
local:
directory: %kernel.root_dir%/../web/images/questions
download_file_adapter:
local:
directory: %kernel.root_dir%/../web/files/download
filesystems:
questions_image_fs:
adapter: questions_image_adapter
download_file_fs:
adapter: download_file_adapter
VichUploaderBundle relies on Doctrine Events like pre persist/update to trgger its upload functionality. When you open existing entity in the admin section and upload new file without changing anything else, doctrine wouldn't dispatch lifecycle events as none of doctrine specific fields are changed.
So whenever new file object passed to the entity you need to update some doctrine specific field value, like updatedAt. Modify setDownloadFile of the entity to:
/**
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile $file
*
* #return File
*/
public function setDownloadFile(File $file = null)
{
$this->downloadFile = $file;
if ($file) {
$this->updatedAt = new \DateTimeImmutable();
}
return $this;
}
Also you need to add updatedAt field and it's mapping in case you didnt.
Take a look at the example on VichUploaderBundle documentation page: https://github.com/dustin10/VichUploaderBundle/blob/master/Resources/doc/usage.md#step-2-link-the-upload-mapping-to-an-entity
UPDATE
Also you need to define form field on downloadFile property instead of path

Symfony Gedmo Blameable not working

Did any one ever find an solution for this issue?
I'm having the same issue.
My config.yml:
# Doctrine Configuration
doctrine:
dbal:
driver: "%database_server%"
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
# if using pdo_sqlite as your database driver:
# 1. add the path in parameters.yml
# e.g. database_path: "%kernel.root_dir%/data/data.db3"
# 2. Uncomment database_path in parameters.yml.dist
# 3. Uncomment next line:
# path: "%database_path%"
orm:
auto_generate_proxy_classes: "%kernel.debug%"
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
#Gedmo Package extension for Symfony and Doctrine
mappings:
gedmo_tree:
type: annotation
prefix: Gedmo\Tree\Entity
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity"
alias: GedmoTree
is_bundle: false
gedmo_sortable:
type: annotation
prefix: Gedmo\Sortable\Entity
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Entity"
alias: GedmoTree
is_bundle: false
[...]
stof_doctrine_extensions:
default_locale: "%locale%"
translation_fallback: true
orm:
default:
timestampable: true
blameable: true
My doctrine_extension.yml is included in the config file:
services:
extension.listener:
class: Omega\HomeBundle\Library\Listener\DoctrineExtensionListener
calls:
- [ setContainer, [#service_container]]
tags:
# loggable hooks user username if one is in security context
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
# Doctrine Extension listeners to handle behaviors
gedmo.listener.tree:
class: Gedmo\Tree\TreeListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ #annotation_reader ] ]
gedmo.listener.sortable:
class: Gedmo\Sortable\SortableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ #annotation_reader ] ]
gedmo.listener.timestampable:
class: Gedmo\Timestampable\TimestampableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ #annotation_reader ] ]
gedmo.listener.loggable:
class: Gedmo\Loggable\LoggableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ #annotation_reader ] ]
gedmo.listener.blameable:
class: Gedmo\Blameable\BlameableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ #annotation_reader ] ]
- [ setUserValue, [ #security.token_storage ] ]
I created myself an trait to handle the created, updated, updated_by and createdby fields:
namespace HomeBundle\Traits;
use Doctrine\ORM\Mapping as ORM;
use Omega\UserBundle\Entity\Users;
use Gedmo\Mapping\Annotation as Gedmo;
trait LogableTrait
{
/**
* #var Users
* #Gedmo\Blameable(on="create")
* #ORM\ManyToOne(targetEntity="UserBundle\Entity\Users")
* #ORM\JoinColumn(name="log_created_by", referencedColumnName="id")
*/
protected $CreatedBy;
/**
* #var Users
* #Gedmo\Blameable(on="update")
* #ORM\ManyToOne(targetEntity="UserBundle\Entity\Users")
* #ORM\JoinColumn(name="log_updated_by", referencedColumnName="id")
*/
protected $UpdatedBy;
/**
* #Gedmo\Timestampable(on="create")
* #ORM\Column(name="created", type="datetime")
* #var \DateTime
*/
protected $created;
/**
* #Gedmo\Timestampable(on="create")
* #ORM\Column(name="updated", type="datetime")
* #var \DateTime
*/
protected $updated;
/**
* #return Users
*/
public function getCreatedBy ()
{
return $this->CreatedBy;
}
/**
* #param Users $CreatedBy
*
* #return $this
*/
public function setCreatedBy (Users $CreatedBy )
{
$this->CreatedBy = $CreatedBy;
return $this;
}
/**
* #return Users
*/
public function getUpdatedBy ()
{
return $this->UpdatedBy;
}
/**
* #param Users $UpdatedBy
*
* #return $this
*/
public function setUpdatedBy (Users $UpdatedBy )
{
$this->UpdatedBy = $UpdatedBy;
return $this;
}
}
But everytime that I use this Bundle I get:
The class 'Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage' was not found in the chain configured namespaces Gedmo\Tree\Entity, Gedmo\Sortable\Entity, JMS\JobQueueBundle\Entity, AccountingBundle\Entity, DocumentsBundle\Entity, EavBundle\Entity, HomeBundle\Entity, UserBundle\Entity, CustomerBundle\Entity, Jns\Bundle\XhprofBundle\Entity
I hope some body can help me.
for any one that Is having th same issue like me that the blamable feature is not working:
My solution was to implement the BlamableListener with an different approach:
namespace HomeBundle\Library;
use Doctrine\Common\NotifyPropertyChanged;
use Gedmo\Exception\InvalidArgumentException;
use Gedmo\Timestampable\TimestampableListener;
use Gedmo\Blameable\Mapping\Event\BlameableAdapter;
use Gedmo\Blameable\Mapping\Driver\Annotation;
/**
* The Blameable listener handles the update of
* dates on creation and update.
*
* #author Gediminas Morkevicius <gediminas.morkevicius#gmail.com>
* #license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
class BlameableListener extends TimestampableListener
{
protected $user;
/**
* Get the user value to set on a blameable field
*
* #param object $meta
* #param string $field
*
* #return mixed
*/
public function getUserValue($meta, $field)
{
if ($meta->hasAssociation($field)) {
if (null !== $this->user && ! is_object($this->user)) {
throw new InvalidArgumentException("Blame is reference, user must be an object");
}
$user = $this->user->getToken()->getUser();
if(!is_object($user))
{
return null;
}
return $user;
}
// ok so its not an association, then it is a string
if (is_object($this->user)) {
if (method_exists($this->user, 'getUsername')) {
return (string) $this->user->getUsername();
}
if (method_exists($this->user, '__toString')) {
return $this->user->__toString();
}
throw new InvalidArgumentException("Field expects string, user must be a string, or object should have method getUsername or __toString");
}
return $this->user;
}
/**
* Set a user value to return
*
* #param mixed $user
*/
public function setUserValue($user)
{
$this->user = $user;
}
/**
* {#inheritDoc}
*/
protected function getNamespace()
{
return __NAMESPACE__;
}
/**
* Updates a field
*
* #param object $object
* #param BlameableAdapter $ea
* #param $meta
* #param $field
*/
protected function updateField($object, $ea, $meta, $field)
{
$property = $meta->getReflectionProperty($field);
$oldValue = $property->getValue($object);
$newValue = $this->getUserValue($meta, $field);
//if blame is reference, persist object
if ($meta->hasAssociation($field) && $newValue) {
$ea->getObjectManager()->persist($newValue);
}
$property->setValue($object, $newValue);
if ($object instanceof NotifyPropertyChanged) {
$uow = $ea->getObjectManager()->getUnitOfWork();
$uow->propertyChanged($object, $field, $oldValue, $newValue);
}
}
}
adjust the service for the blamable:
gedmo.listener.blameable:
class: HomeBundle\Library\BlameableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ #annotation_reader ] ]
- [ setUserValue, [ #security.token_storage ] ]
you need to copy the mapping library to the same location as the listener itself. Adjust the namespaces and it works. It seems that some structures changed in symfony 2.7 so the plugin no longer works out of the box.
If you want to update the updated_by field, you must specify the field so that when you update it, do so in updated_by. For example:
/**
* #var \DateTime $updated
*
* #Gedmo\Timestampable(on="update")
* #ORM\Column(type="datetime", nullable=true)
*/
protected $updated;
/**
* #var string $updatedBy
*
* #Gedmo\Blameable(on="update", field="updated")
* #ORM\Column(type="string", nullable=true)
*/
protected $updatedBy;
Pay attention to field="updated"
Just create DoctrineExtensionSubscriber
Manually tag the listeners
Gedmo\Loggable\LoggableListener:
tags:
- { name: doctrine_mongodb.odm.event_subscriber }
Create DoctrineExtensionSubscriber
<?php
namespace App\EventSubscriber;
use Gedmo\Blameable\BlameableListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class DoctrineExtensionSubscriber implements EventSubscriberInterface
{
/**
* #var BlameableListener
*/
private $blameableListener;
/**
* #var TokenStorageInterface
*/
private $tokenStorage;
/**
* #var TranslatableListener
*/
private $translatableListener;
/**
* #var LoggableListener
*/
private $loggableListener;
public function __construct(
BlameableListener $blameableListener,
TokenStorageInterface $tokenStorage,
TranslatableListener $translatableListener,
LoggableListener $loggableListener
) {
$this->blameableListener = $blameableListener;
$this->tokenStorage = $tokenStorage;
$this->translatableListener = $translatableListener;
$this->loggableListener = $loggableListener;
}
public static function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => 'onKernelRequest',
KernelEvents::FINISH_REQUEST => 'onLateKernelRequest'
];
}
public function onKernelRequest(): void
{
if ($this->tokenStorage !== null &&
$this->tokenStorage->getToken() !== null &&
$this->tokenStorage->getToken()->isAuthenticated() === true
) {
$this->blameableListener->setUserValue($this->tokenStorage->getToken()->getUser());
}
}
public function onLateKernelRequest(FinishRequestEvent $event): void
{
$this->translatableListener->setTranslatableLocale($event->getRequest()->getLocale());
}
}

No entity manager defined for class

I am trying to just display a list. My Supplier and Property list were displaying until I implemented the product list :(
Now I get this error with the Supplier and Property lists but not for the newly added product list:
Here is my app/config.yml:
imports:
- { resource: parameters.yml }
- { resource: security.yml }
- { resource: services.yml }
parameters:
sylius.locale: "%locale%"
framework:
#esi: ~
translator: { fallbacks: ["%locale%"] }
secret: "%secret%"
router:
resource: "%kernel.root_dir%/config/routing.yml"
strict_requirements: ~
form: ~
csrf_protection: ~
validation: { enable_annotations: true }
templating:
engines: ['twig']
#assets_version: SomeVersionScheme
default_locale: "%locale%"
trusted_hosts: ~
trusted_proxies: ~
session:
storage_id: session.storage.php_bridge
# handler_id set to null will use default session handler from php.ini
handler_id: ~
fragments: ~
http_method_override: true
# Twig Configuration
twig:
debug: "%kernel.debug%"
strict_variables: "%kernel.debug%"
form:
resources:
- 'SonataCoreBundle:Form:datepicker.html.twig'
# Assetic Configuration
assetic:
debug: "%kernel.debug%"
use_controller: false
#java: /usr/bin/java
node: /usr/bin/node
node_paths: [ /usr/lib/node_modules, %kernel.root_dir%/../node_modules ]
filters:
autoprefixer:
bin: %kernel.root_dir%/../node_modules/autoprefixer/autoprefixer
cssrewrite: ~
less:
apply_to: "\.less$"
bin: %kernel.root_dir%/../node_modules/less
uglifycss:
bin: %kernel.root_dir%/../node_modules/uglifycss
uglifyjs:
bin: %kernel.root_dir%/../node_modules/uglifyjs
#closure:
# jar: "%kernel.root_dir%/Resources/java/compiler.jar"
#yui_css:
# jar: "%kernel.root_dir%/Resources/java/yuicompressor-2.4.7.jar"
# Doctrine Configuration
doctrine:
dbal:
driver: "%database_driver%"
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
mapping_types:
enum: string
orm:
auto_generate_proxy_classes: "%kernel.debug%"
entity_managers:
default:
connection: default
auto_mapping: true
dql:
string_functions:
HS_CONCAT_ADDRESS: AppBundle\DQL\HSConcatAddress
# Swiftmailer Configuration
swiftmailer:
transport: "%mailer_transport%"
host: "%mailer_host%"
username: "%mailer_user%"
password: "%mailer_password%"
spool: { type: memory }
# FOS UserBundle Configuration
fos_user:
registration:
form:
type: hs_user_registration
db_driver: orm
firewall_name: main
user_class: AppBundle\Entity\User
service:
user_manager: app.security.user_manager
# Doctrine migration bundle Configuration
doctrine_migrations:
dir_name: %kernel.root_dir%/DoctrineMigrations
namespace: Application\Migrations
table_name: migration_versions
name: Application Migrations
# Sonata Admin Configuration
sonata_admin:
options:
use_select2: false # disable select2
title: App
title_logo: bundles/sonataadmin/logo_title.png
# Override default template
templates:
show: AppBundle:Admin/Backend:show.html.twig
edit: AppBundle:Admin/Backend:edit.html.twig
layout: AppBundle:Common/Admin:standard_layout.html.twig
security:
# handler: sonata.admin.security.handler.role
#sonata_intl:
# timezone:
# locales:
# en_GB: Europe/London
# default: Europe/London
# Sonata Block Configuration
sonata_block:
default_contexts: [cms]
blocks:
sonata.user.block.menu: # used to display the menu in profile pages
sonata.user.block.account: # used to display menu option (login option)
sonata.admin.block.admin_list:
contexts: [admin]
sonata.admin.block.search_result:
contexts: [admin]
knp_menu:
# use "twig: false" to disable the Twig extension and the TwigRenderer
twig:
template: knp_menu.html.twig
# if true, enables the helper for PHP templates
templating: false
# the renderer to use, list is also available by default
default_renderer: twig
# STOF Doctrine Extensions Configuration
stof_doctrine_extensions:
default_locale: %locale%
orm:
default:
translatable: true
sluggable: true
timestampable: true
a2lix_translation_form:
locale_provider: default # [1]
locales: ["%locale%"] # [1-a]
default_locale: "%locale%" # [1-b]
manager_registry: doctrine # [2]
# Sylius Archetype
sylius_archetype:
classes:
product:
subject: Sylius\Component\Product\Model\Product
attribute: Sylius\Component\Product\Model\Attribute
option: Sylius\Component\Product\Model\Option
archetype:
model: Sylius\Component\Product\Model\Archetype
repository: Sylius\Bundle\ResourceBundle\Doctrine\ORM\TranslatableEntityRepository
translatable:
targetEntity: Sylius\Component\Product\Model\ArchetypeTranslation
archetype_translation:
model: Sylius\Component\Product\Model\ArchetypeTranslation
sylius_product:
driver: doctrine/orm
classes:
product:
model: AppBundle\Entity\Product
controller: AppBundle\Controller\Backend\ProductController
form:
default: AppBundle\Form\Type\ProductType
translatable:
targetEntity: AppBundle\Entity\ProductTranslation
product_translation:
model: AppBundle\Entity\ProductTranslation
form:
default: AppBundle\Form\Type\ProductTranslationType
#sylius_resource:
# resources:
# app.backend:
# driver: doctrine/orm
# object_manager: default
# classes:
# controller: AppBundle\Controller\Backend\ResourceController
# model: AppBundle\Entity\Product
# Sylius Attribute
sylius_attribute:
driver: doctrine/orm
# Sylius Locale
sylius_locale:
driver: doctrine/orm
# Sylius Translation
sylius_translation:
default_mapping:
translatable:
field: translations
currentLocale: currentLocale
fallbackLocale: fallbackLocale
translation:
field: translatable
locale: locale
# Sylius Variation
sylius_variation:
driver: doctrine/orm
classes:
product:
variant:
model: AppBundle\Entity\ProductVariant
form: AppBundle\Form\Type\ProductVariantType
My admin class:
namespace AppBundle\Admin;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Form\FormMapper;
use Symfony\Component\EventDispatcher\Event;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use Sonata\AdminBundle\Admin\AdminInterface;
use Sonata\AdminBundle\Route\RouteCollection;
use Sonata\AdminBundle\Form\Type;
use Sonata\AdminBundle\Validator\ErrorElement;
use AppBundle\Entity\Suppliers as Suppliers;
use AppBundle\Form\Type\CreateSupplierForm;
use Symfony\Component\DependencyInjection\ContainerInterface;
class SupplierAdmin extends Admin
{
/**
* {#inheritdoc}
*/
protected $baseRouteName = 'Admin\SupplierAdmin';
/**
* {#inheritdoc}
*/
protected $baseRoutePattern = 'supplier';
/**
* #var ContainerInterface
*/
private $container;
public function setContainer (ContainerInterface $container)
{
$this->container = $container;
}
/**
* #param \Sonata\AdminBundle\Show\ShowMapper $showMapper
*
* #return void
*/
protected function configureShowFields(ShowMapper $showMapper)
{
$showMapper
->with('Supplier Details')
->add('id')
->add('name')
->add('created')
->end();
if ($this->getSubject()->getType() == 'LPE') {
$showMapper->with('Property Expert')
->add('LpeFirstName', null, array('label' => 'First Name'))
->add('LpeLastName', null, array('label' => 'Last Name'))
->add('LpeEmail', null, array('label' => 'Email'))
->add('ContractType', null, array('label' => 'Type'))
->add('regions', 'entity', array(
'class' => 'AppBundle:Regions',
"multiple" => true,
'label' => 'Regions Covered'
))
->end();
}
}
/**
* #param \Sonata\AdminBundle\Datagrid\ListMapper $listMapper
*
* #return void
*/
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->add('id')
->add('Type')
->addIdentifier('name')
->add('created', 'date', array(
'pattern' => 'dd/MM/Y # H:m',
'locale' => 'en_GB',
'timezone' => 'Europe/London',
))
// add custom action links
->add('_action', 'actions', array(
'actions' => array(
'show' => array(),
'delete' => array()
)
));
}
/**
* #param \Sonata\AdminBundle\Datagrid\DatagridMapper $datagridMapper
*
* #return void
*/
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper
->add('type', null, array(),
'choice',
array('choices' => Suppliers::getTypes()))
->add('name');
}
protected function configureRoutes(RouteCollection $collection)
{
parent::configureRoutes($collection);
$collection->add('create', 'create', array(
'_controller' => 'AppBundle:Backend/SupplierAdmin:createSupplier'
)
);
$collection->add('show', $this->getRouterIdParameter() . '/show', array(
'_controller' => 'AppBundle:Backend/SupplierAdmin:show',
)
);
$collection->add('delete', $this->getRouterIdParameter() . '/delete', array(
'_controller' => 'AppBundle:Backend/SupplierAdmin:delete',
)
);
}
/**
* #return array
*/
public function getBatchActions()
{
// Disable all batch actions for now
$actions = array();
return $actions;
}
/**
* {#inheritdoc}
*/
public function createQuery($context = 'list')
{
$query = parent::createQuery($context);
// Default Alias is "o"
$query->orderBy('o.id', 'DESC');
$query->setSortOrder('DESC');
return $query;
}
public function preUpdate($supplier)
{
$supplier->setDeleted(0);
return $supplier;
}
public function preCreate($supplier)
{
$supplier->setDeleted(0);
return $supplier;
}
}
And lastly my Supplier entity:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Suppliers
*
* #ORM\Table(
* name="suppliers",
* indexes={
* #ORM\Index(
* name="supplier_name", columns={"name"}
* )
* })
* #ORM\Entity
* #ORM\HasLifecycleCallbacks()
*/
class Suppliers
{
const TYPE_INTERNAL = 'internal';
const TYPE_EXTERNAL = 'external';
const TYPE_LPE = 'lpe';
protected static $types = array(
self::TYPE_INTERNAL => 'Internal',
self::TYPE_EXTERNAL => 'External',
self::TYPE_LPE => 'LPE'
);
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false, options={"unsigned":true})
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #Assert\NotNull()
* #ORM\Column(name="type", type="string", nullable=false, options={"default":"lpe"})
*/
private $type;
/**
* #var string
*
* #Assert\NotNull()
* #ORM\Column(name="name", type="string", nullable=false)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="created", type="datetime")
*/
private $created;
/**
* #var string
*
* #ORM\Column(name="updated", nullable=true, type="datetime")
*/
private $updated;
/**
* #var string
*
* #ORM\Column(name="deleted", type="boolean", options={"default":0})
*/
private $deleted;
/**
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Regions")
* #ORM\JoinTable(name="supplier_regions",
* joinColumns={
* #ORM\JoinColumn(
* name="supplier_id",
* referencedColumnName="id"
* )
* },
* inverseJoinColumns={
* #ORM\JoinColumn(
* name="region_id",
* referencedColumnName="postcode"
* )
* }
* )
*/
private $regions;
private $lpe;
public function __construct()
{
$this->regions = new ArrayCollection();
}
public function getRegions()
{
return $this->regions;
}
public function setRegions($regions)
{
$this->regions = $regions;
return $this;
}
public function getLpe()
{
return $this->lpe;
}
public function setLpe($lpe)
{
$this->lpe = $lpe;
return $this;
}
/**
* #return string
*/
public function getLpeFirstName()
{
return $this->lpe->getUser()->getFirstName();
}
/**
* #return string
*/
public function getLpeLastName()
{
return $this->lpe->getUser()->getLastName();
}
/**
* #return string
*/
public function getLpeEmail()
{
return $this->lpe->getUser()->getEmail();
}
public function getContractType()
{
return $this->lpe->getContractType();
}
public function getUser()
{
return $this->lpe->getUser();
}
public function setUser($user)
{
$this->lpe->setUser($user);
return $this;
}
public function setFirstName($name)
{
$this->user->setFirstName($name);
return $this;
}
public function getFirstName()
{
return $this->user->getFirstName();
}
public function setLastName($name)
{
$this->user->setLastName($name);
return $this;
}
public function getLastName()
{
return $this->getUser()->getLastName();
}
public function setEmail($email)
{
$this->getUser()->setEmail($email);
return $this;
}
public function getEmail()
{
return $this->getUser()->getEmail();
}
/**
* #var string
*/
private $entityName;
/**
* Get id
*
* #return integer
*/
public function nullId()
{
return $this->id =null;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set type
*
* #param string $type
* #return User
*/
public function setType($type)
{
$this->type = strtolower($type);
return $this;
}
/**
* Get type
*
* #return string
*/
public function getType()
{
if ($this->type == 'lpe') {
return strtoupper($this->type);
}
return ucwords($this->type);
}
/**
* Set name
*
* #param string $name
* #return User
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set created
*
* #param string $created
* #return $this
*/
public function setCreated($created)
{
if (!empty($created)) {
$this->created = $created;
} else {
$this->created = new \DateTime("now");
}
return $this;
}
/**
* Get created
*
* #return string
*/
public function getCreated()
{
return $this->created;
}
/**
* Set updated
*
* #param string $updated
* #return User
*/
public function setUpdated($updated)
{
$this->updated = $updated;
return $this;
}
/**
* Get updated
*
* #return string
*/
public function getUpdated()
{
return $this->updated;
}
/**
* Set deleted
*
* #param boolean $deleted
* #return User
*/
public function setDeleted($deleted)
{
$this->deleted = $deleted;
return $this;
}
/**
* Get deleted
*
* #return boolean
*/
public function getDeleted()
{
return $this->deleted;
}
/**
* #ORM\PrePersist()
*/
public function onPrePersist()
{
$this->created = new \DateTime();
$this->updated = new \DateTime();
if ($this->deleted === null) {
$this->deleted = 0;
}
if ($this->type === null) {
$this->type = 'lpe';
}
if ($this->name === null) {
$this->name = '';
}
}
/**
* #ORM\PreUpdate()
*/
public function onPreUpdate()
{
$this->created = new \DateTime();
$this->updated = new \DateTime();
if ($this->deleted === null) {
$this->deleted = 0;
}
}
/**
* #return array
*/
public static function getTypes()
{
return self::$types;
}
/**
* Set entityName
*
* #param string $name
* #return User
*/
public function setEntityName($name)
{
$this->entityName = $name;
return $this;
}
public function __toString()
{
if ($this->getUser() === null) {
return 'Supplier' . ($this->id !== null ? ' #' . $this->id.'' : '' );
}
return (string) ($this->id !== null ? '#' . $this->id.' ' : '' ) . $this->getUser()->getEmail();
}
}
Any suggestions? :)
I think it is in your configuration file
doctrine:
[...]
orm:
[...]
mappings:
AppBundle:
type: ~
dir: "Entity"
prefix: "AppBundle\Entity"
is_bundle: ~
If it is not working, look over your namespaces, I think (but not sure) it should be like PROJECT\BUNDLE\Entity and not just BUNDLE\Entity (for both namespace and use statements)
You have to set your doctrine configuration to add your bundle to the ORM :
# Doctrine Configuration
doctrine:
dbal:
driver: pdo_mysql
host: '%database_host%'
port: '%database_port%'
dbname: '%database_name%'
user: '%database_user%'
password: '%database_password%'
charset: UTF8
orm:
auto_generate_proxy_classes: '%kernel.debug%'
entity_managers:
default:
mappings:
ApplicationSonataUserBundle: ~
SonataUserBundle: ~
FOSUserBundle: ~
**YourBundleNameBundle**: ~

Categories