Faced the problem of setting up the admin panel. I do everything according to the documentation, there are no errors, Dashboard opens and everything is empty. The project is simple as a door - the simplest blog. Nothing shows up - neither the names of categories nor posts. The whole brain broke itself.
config.yml
imports:
- { resource: parameters.yml }
- { resource: security.yml }
- { resource: services.yml }
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
locale: en
framework:
#esi: ~
translator: { fallbacks: [en] }
secret: '%secret%'
router:
resource: '%kernel.project_dir%/app/config/routing.yml'
strict_requirements: ~
form: ~
csrf_protection: ~
validation: { enable_annotations: true }
#serializer: { enable_annotations: true }
templating:
engines: ['twig']
default_locale: '%locale%'
trusted_hosts: ~
session:
# https://symfony.com/doc/current/reference/configuration/framework.html#handler-id
handler_id: session.handler.native_file
save_path: '%kernel.project_dir%/var/sessions/%kernel.environment%'
fragments: ~
http_method_override: true
assets: ~
php_errors:
log: true
# Twig Configuration
twig:
debug: '%kernel.debug%'
strict_variables: '%kernel.debug%'
# Doctrine Configuration
doctrine:
dbal:
driver: pdo_mysql
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.project_dir%/var/data/data.sqlite'
# 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
# Swiftmailer Configuration
swiftmailer:
transport: '%mailer_transport%'
host: '%mailer_host%'
username: '%mailer_user%'
password: '%mailer_password%'
spool: { type: memory }
#Cache
doctrine_cache:
providers:
my_markdown_cache:
type: file_system
file_system:
directory: /tmp/doctrine_cache
sonata_admin:
title: My Blog Admin
sonata_block:
default_contexts: [cms]
blocks:
sonata.admin.block.admin_list:
contexts: [admin]
services.yml
parameters:
#parameter_name: value
services:
# default configuration for services in *this* file
_defaults:
# automatically injects dependencies in your services
autowire: true
# automatically registers your services as commands, event subscribers, etc.
autoconfigure: true
# this means you cannot fetch services directly from the container via $container->get()
# if you need to do this, you can override this setting on individual services
public: false
# makes classes in src/AppBundle available to be used as services
# this creates a service per class whose id is the fully-qualified class name
AppBundle\:
resource: '../../src/AppBundle/*'
# you can exclude directories or files
# but if a service is unused, it's removed anyway
exclude: '../../src/AppBundle/{Entity,Repository,Tests}'
# controllers are imported separately to make sure they're public
# and have a tag that allows actions to type-hint services
AppBundle\Controller\:
resource: '../../src/AppBundle/Controller'
public: true
tags: ['controller.service_arguments']
# add more services, or override services that need manual wiring
# AppBundle\Service\ExampleService:
# arguments:
# $someArgument: 'some_value'
services:
app.admin.category:
class: AppBundle\Admin\CategoryAdmin
arguments: [~, AppBundle\Entity\Category, ~]
tags:
- { name: sonata.admin, manager_type: orm, label: Category }
public: true
app.admin.articles:
class: AppBundle\Admin\ArticlesAdmin
arguments: [~, AppBundle\Entity\Articles, ~]
tags:
- { name: sonata.admin, manager_type: orm, label: Blog post }
public: true
CategoryAdmin class
namespace AppBundle\Admin;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Show\ShowMapper;
class CategoryAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper->add('name', 'text', array('label' => 'Название'));
}
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper->add('name');
}
protected function configureListFields(ListMapper $listMapper)
{
$listMapper->addIdentifier('name');
}
protected function configureShowFields(ShowMapper $showMapper)
{
$showMapper
->add('name');
}
}
Category Entity Class
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Category
*
* #ORM\Table(name="category")
* #ORM\Entity(repositoryClass="AppBundle\Repository\CategoryRepository")
*/
class Category
{
/**
* #ORM\OneToMany(targetEntity="Articles", mappedBy="category")
*/
private $blogPosts;
public function __construct()
{
$this->blogPosts = new ArrayCollection();
}
public function getBlogPosts()
{
return $this->blogPosts;
}
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Category
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
The problem is solved. The solution was not in the documentation for the bundle but in the documentation of the symphony
Related
I am a newbie in using Symfony2.8 and I got a validation problem.
This is how my code from dependency injection looks like --
<?php
namespace AppBundle\DependencyInjection;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
class AppExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container) {
$r = new \ReflectionClass(get_class($this));
$dir = dirname($r->getFileName());
$loader = new YamlFileLoader($container, new FileLocator($dir.'/../Resources/config'));
$loader->load('validation.yml');
}
}
And then I am trying to validate that those two columns are unique. (UniqueConstraint).
# src/AppBundle/Resources/config/validation.yml
AppBundle\Entity\StaticContent:
constraints:
- Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity:
fields: [build, name]
With StaticContent in the Entity and it complains that it can't find the StaticContent.
nebo#localhost:/var/www/html/iclei$ php app/console doctrine:schema:update --dump-sql
[Symfony\Component\DependencyInjection\Exception\InvalidArgumentException]
There is no extension able to load the configuration for "AppBundle\Entity\StaticContent" (in /var/www/html/iclei/src/AppBundle/DependencyInjection/../Resources/config/validation.yml). Looked for nam
espace "AppBundle\Entity\StaticContent", found none
What am I doing wrong ?
Also I have inside the
app/config/config.yml
framework:
#esi: ~
#translator: { fallbacks: ['%locale%'] }
secret: '%secret%'
router:
resource: '%kernel.root_dir%/config/routing.yml'
strict_requirements: ~
form: ~
csrf_protection: ~
validation: { enabled: true, enable_annotations: true }
And this is the structure of the Bundle which we are building.
DIR/src/AppBundle/Resources/config/doctrine/StaticContent.orm.yml
DIR/src/AppBundle/DependencyInjection/AppExtension.php
This is my doctrine configuration:
# Doctrine Configuration
doctrine:
dbal:
driver: pdo_mysql
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:
entity_managers:
default:
mappings:
AppBundle: ~
auto_generate_proxy_classes: '%kernel.debug%'
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
On I13 request I add my entity.
<?php
namespace AppBundle\Entity;
/**
* StaticContent
*/
class StaticContent
{
/**
* #var int
*/
private $id;
/**
* #var int
*/
private $build;
/**
* #var string
*/
private $name;
/**
* #var string
*/
private $type;
/**
* #var string
*/
private $data;
With their getters and setters.
AppBundle\Entity\StaticContent:
type: entity
table: null
repositoryClass: AppBundle\Repository\StaticContentRepository
id:
id:
type: integer
id: true
generator:
strategy: AUTO
fields:
name:
type: string
length: 255
data:
type: blob
manyToOne:
build:
targetEntity: AppBundle\Entity\Build
inversedBy: staticContents
joinColumn:
name: build_id
referencedColumnName: id
lifecycleCallbacks: { }
Please try to enable bundle under doctrine configuration and tell is it works:
# 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:
auto_mapping: true
mappings:
YOURBundle: ~
Change AppExtension:
<?php
namespace AppBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
class AppExtension extends Extension {
/**
* {#inheritdoc}
*/
public function load(array $configs, ContainerBuilder $container) {
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
}
}
I am using symfony LTS with master FOS:elastica and master jms_serializer.
Config
fos_elastica:
clients:
default: { host: %elastic_host%, port: %elastic_port% }
serializer:
callback_class: FOS\ElasticaBundle\Serializer\Callback
serializer: serializer
indexes:
c_search:
client: default
types:
adverts:
mappings:
id:
type: integer
user:
type: string
Media:
type: nested
properties:
urlMedia: ~
persistence:
driver: orm
model: WebSite\MyBundle\Entity\Advert
provider: ~
listener: ~
finder: ~
Populate
php app/console fos:elastica:populate
This command work without any problem before I added the lines below. When I try to run "php app/console fos:elastica:populate" there is an infinite loop since I have added these lines:
serializer:
callback_class: FOS\ElasticaBundle\Serializer\Callback
serializer: serializer
Troubleshooting:
After this I made a simple request (without serialize because of my problem) like this on my controller :
public function testAction()
{
$repositoryManager = $this->container->get('fos_elastica.manager');
$repository = $repositoryManager->getRepository('WebSiteMyBundle:Advert');
$data = json_encode($repository->find('68200'));
return $this->render('WebSiteMyBundle:Default:test.html.twig', array(
'test'=> $data,
));
}
On my result test there is like 5 empty arrays. I know the result is good since there is normaly 5 response in my raw request but I can't find the solution to show the real content if anyone have an idea.
Ok, I was right in my comment, this issue was related to serializer and is quite logical.
Actually, if you do not specify anything, elastic will index every property and every relation in cascade which is a problem when having some circular references.
Here is what you could do to fix you problem :
First, add some properties in your group :
<?php
namespace WebSite\MyBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
/**
* Advert
*
* #ORM\Table(name="advert")
*/
class Advert
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=55)
* #Serializer\Groups({"elastica"})
*/
private $title;
...
Then, add this in your config file :
fos_elastica:
clients:
default: { host: %elastic_host%, port: %elastic_port% }
serializer:
callback_class: FOS\ElasticaBundle\Serializer\Callback
serializer: serializer
indexes:
c_search:
client: default
types:
adverts:
serializer:
groups: [elastica]
mappings:
id:
type: integer
user:
type: string
Media:
type: nested
properties:
urlMedia: ~
persistence:
driver: orm
model: WebSite\MyBundle\Entity\Advert
provider: ~
listener: ~
finder: ~
Then, you shouldn't have any circular reference and only having title property in your elasticsearch index.
Good luck.
I'm trying to translate entities with gedmo doctrine extensions.
https://github.com/Atlantic18/DoctrineExtensions
I'm using yml as orm mapping file (auto generating entities).
orm.yml:
CS\ContentBundle\Entity\Post:
type: entity
table: posts
repositoryClass: CS\ContentBundle\Entity\PostRepository
gedmo:
soft_deleteable:
field_name: deleted_at
translation:
locale: locale
fields:
id:
type: integer
length: 11
id: true
generator:
strategy: AUTO
title:
type: string
length: 500
gedmo:
- translatable
slug:
type: string
length: 500
gedmo:
translatable: {}
slug:
separator: -
fields:
- title
I can translate the title without a problem. But slug is not working...
Normally, on default language (tr), slug auto generated without any generation process by me.
Entity file:
<?php
namespace CS\ContentBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Translatable\Translatable;
use Gedmo\Mapping\Annotation as Gedmo;
use APY\DataGridBundle\Grid\Mapping as GRID;
/**
* Post
* #Gedmo\SoftDeleteable(fieldName="deleted_at", timeAware=false)
* #GRID\Source(columns="id,title,is_active,created_at,updated_at")
*/
class Post implements Translatable
{
/**
* #var integer
*/
private $id;
/**
* #var string
* #Gedmo\Translatable
* #GRID\Column(title="Başlık", filterable=true)
*/
private $title;
/**
* #var string
* #Gedmo\Translatable
*/
private $slug;
/**
* #Gedmo\Locale
*/
private $locale;
public function setLocale($locale)
{
$this->locale = $locale;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
* #return Post
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set slug
*
* #param string $slug
* #return Post
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Get slug
*
* #return string
*/
public function getSlug()
{
return $this->slug;
}
}
There is a part in documentation:
https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/sluggable.md#using-translationlistener-to-translate-our-slug
I don't know how to apply these listeners.
What should i do to translate the slug automatically?
EDIT
My config.yml for doctrine and stof_doctrine_extensions:
doctrine:
dbal:
default_connection: default
connections:
default:
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%"
auto_mapping: true
metadata_cache_driver: redis
query_cache_driver: redis
filters:
softdeleteable:
class: Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter
enabled: true
mappings:
StofDoctrineExtensionsBundle: ~
gedmo_translatable:
type: annotation
prefix: Gedmo\Translatable\Entity
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity"
alias: GedmoTranslatable # this one is optional and will default to the name set for the mapping
is_bundle: false
gedmo_tree:
type: annotation
prefix: Gedmo\Tree\Entity
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity"
alias: GedmoTree # this one is optional and will default to the name set for the mapping
is_bundle: false
stof_doctrine_extensions:
default_locale: "%locale%"
translation_fallback: true
orm:
default:
tree: true
sluggable: true
translatable: true
timestampable: true
softdeleteable: true
After a bit of struggling, I think I've managed to find a solution to your problem. Let's get down to business.
First of all, I've noticed you're using StofDoctrineExtensionsBundle which is a wrapper of the Gedmo Doctrine2 extensions.
It might have seemed easier to install/use but in the long run it complicates matters. In this case, for example, in order to solve your problem you have to modify listeners and with that bundle the only solution I can come up with is to hack the code and change the priority of the service manually. This leads to the first solution:
First solution: hack the bundle (not a good one)
The services can be found in
StofDoctrineExtensionsBundle/Resources/config/listeners.xml
You need to find and modify the sluggable service and increase its priority. That way, this service will execute before the translatable service. The slug would be created first and translatable would store it nicely.
Modification (haven't tried it though I think it might work):
<service id="stof_doctrine_extensions.listener.sluggable" class="%stof_doctrine_extensions.listener.sluggable.class%" public="false">
<tag name="kernel.cache_warmer" priority="1" />
<call method="setAnnotationReader">
<argument type="service" id="annotation_reader" />
</call>
</service>
However, I don't like this solution. To be honest, I don't like wrappers. I'm going to give you another solution that I find more satisfactory (and I tried it and works).
Second solution (and the best one)
First off, get rid of StofDoctrineExtensionsBundle. Secondly, install Gedmo Doctrine2 extensions (how to do it here).
You'll notice that in order to install the extensions you had to create a file in your bundle containing the necessary services. That is good. Go to that file and modify the priority for the sluggable service:
gedmo.listener.sluggable:
class: Gedmo\Sluggable\SluggableListener
tags:
- { name: doctrine.event_subscriber, connection: default, priority: 1 }
calls:
- [ setAnnotationReader, [ #annotation_reader ] ]
And now it is ready.
These are my yml and php for the entity Post (I think they are really similar to yours but can differ):
YML
DSG\AcmeBundle\Entity\Post:
type: entity
table: posts
gedmo:
soft_deleteable:
field_name: deleted_at
translation:
locale: locale
fields:
id:
type: integer
length: 11
id: true
generator:
strategy: AUTO
title:
type: string
length: 255
gedmo:
- translatable
slug:
type: string
length: 255
unique: true
gedmo:
translatable: {}
slug:
separator: _
style: camel
fields:
- title
And the PHP
namespace DSG\AcmeBundle\Entity;
use Gedmo\Translatable\Translatable;
class Post implements Translatable
{
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $title;
/**
* #var string
*/
private $slug;
/**
* #var string
*/
private $locale;
public function setTranslatableLocale($locale)
{
$this->locale = $locale;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
* #return Post
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set slug
*
* #param string $slug
* #return Post
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Get slug
*
* #return string
*/
public function getSlug()
{
return $this->slug;
}
}
When you create and entity and store it in the database, it will create the slug and store the title and the slug in the translation table (if you change the locale).
If you try:
$em = $this->getDoctrine()->getEntityManager();
$article = new Post();
$article->setTitle('the title');
$em->persist($article);
$em->flush();
$article = $this->getDoctrine()->getRepository('DSGMDayBundle:Post')->find(1);
$article->setTitle('my title in de');
$article->setTranslatableLocale('de_de'); // change locale
$em->persist($article);
$em->flush();
You'll see that your translation table gets two rows, one for the title and the other for the slug for the local de_de.
Hope it helps and kind regards.
Something weird to me : Your ORM config by entity is done in 2 files (yaml and annotation) ?
Listeners look to be well loaded, in the proper order. Maybe it's the entity field config in orm.yml. Try to invert translatable and sluggable on the field slug:
CS\ContentBundle\Entity\Post:
type: entity
table: posts
repositoryClass: CS\ContentBundle\Entity\PostRepository
gedmo:
soft_deleteable:
field_name: deleted_at
translation:
locale: locale
fields:
id:
type: integer
length: 11
id: true
generator:
strategy: AUTO
title:
type: string
length: 500
gedmo:
- translatable
slug:
type: string
length: 500
gedmo:
slug:
separator: -
fields:
- title
translatable: {}
Your configurations is look like correct. However, did you try to update vendors? Maybe some files may be corrupted.
If update process does not work, could you full compare configurations with mine:
doctrine:
dbal:
driver: "%database_driver%"
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
orm:
auto_generate_proxy_classes: "%kernel.debug%"
auto_mapping: true
dql:
string_functions:
GroupConcat: DoctrineExtensions\Query\Mysql\GroupConcat
IF: DoctrineExtensions\Query\Mysql\IfElse
filters:
softdeleteable:
class: Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter
enabled: true
mappings:
StofDoctrineExtensionsBundle: ~
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
translatable:
type: annotation
alias: Gedmo
prefix: Gedmo\Translatable\Entity
# make sure vendor library location is correct
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity"
And make sure that you do not override the gedmo.listener.translatable on your services.yml. If there is any code that overrides the default one, remove it. Default configuration:
gedmo.listener.translatable:
class: Gedmo\Translatable\TranslatableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ #annotation_reader ] ]
- [ setDefaultLocale, [ %locale% ] ]
- [ setTranslationFallback, [ false ] ]
I use StofDoctrineExtensionsBundle for sluggable and translatable and had the same problem.
I have searched and tried a while and found a solution working for me:
Entity:
the slug field gets generated from the name field
use Gedmo\Mapping\Annotation as Gedmo;
use Gedmo\Translatable\Translatable;
/**
* #var string
*
* #Gedmo\Translatable
* #ORM\Column(name="name", type="string", length=150, unique=true)
*/
private $name;
/**
* #Gedmo\Translatable
* #Gedmo\Slug(fields={"name"}, updatable=true)
* #ORM\Column(type="string", unique=true)
*/
private $slug;
config.yml: here you have to set persist_default_translation: true
https://github.com/Atlantic18/DoctrineExtensions/issues/542#issuecomment-12983553
parameters:
locale: en
doctrine:
orm:
auto_generate_proxy_classes: '%kernel.debug%'
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
mappings:
gedmo_translatable:
type: annotation
prefix: Gedmo\Translatable\Entity
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity"
alias: GedmoTranslatable # (optional) it will default to the name set for the mapping
is_bundle: false
gedmo_translator:
type: annotation
prefix: Gedmo\Translator\Entity
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/Entity"
alias: GedmoTranslator # (optional) it will default to the name set for the mapping
is_bundle: false
stof_doctrine_extensions:
default_locale: '%locale%'
translation_fallback: true
persist_default_translation: true # I think this does the trick
orm:
default:
sluggable: true
translatable: true
DefaultController use ParamConverter for calling a query which returns correct entity for current locale
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
/**
* #Route("/entry/{slug}", name="entry_detail")
* #ParamConverter("entry", class="AppBundle:Entry", options={"repository_method" = "findOneByCriteria"})
*/
public function entryDetailAction(Request $request, Entry $entry)
{
return $this->render('frontend/entry.html.twig', [
'entry' => $entry,
]);
}
Entity Repository with the query method to return entity
/**
* Find an entry by criteria
* Need this special function, because of translatable
* https://github.com/stof/StofDoctrineExtensionsBundle/issues/232
*
* #param $params
* #return mixed
*/
public function findOneByCriteria(array $params)
{
$query = $this->createQueryBuilder('e');
$i = 0;
foreach ($params as $column => $value) {
if ($i < 1) {
$query->where("e.$column = :$column");
} else {
$query->andWhere("e.$column = :$column");
}
$query->setParameter($column, $value);
$i++;
}
$query = $query->getQuery();
$query->setHint(\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER, 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker');
return $query->getOneOrNullResult();
}
I hope this example helps someone.
I'm starting with Symfony and I have Entity "Post"
<?php
namespace My\BackendBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* Post
*
* #ORM\Table()
* #ORM\Entity
*/
class Post
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #var string
*
* #Gedmo\Slug(fields={"title"})
* #ORM\Column(type="string", length=255, unique=true)
*/
private $slug;
/**
* #var string
*
* #ORM\Column(name="text", type="text")
*/
private $text;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
* #return Post
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set text
*
* #param string $text
* #return Post
*/
public function setText($text)
{
$this->text = $text;
return $this;
}
/**
* Get text
*
* #return string
*/
public function getText()
{
return $this->text;
}
/**
* Set slug
*
* #param string $slug
* #return Post
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Get slug
*
* #return string
*/
public function getSlug()
{
return $this->slug;
}
}
I've generated table in database:
app/console doctrine:schema:update --force
And CRUD panel:
app/console doctrine:generate:crud
with options:
Entity: MyBackendBundle:Post
"write" action: yes
But as you can see, I have "slug" field and I want Doctrine to auto-generate it. But default CRUD panel at "/post/new" have inputs for all 3 fields (title, slug and text). And after hours of searching for solution only one I can do is manually delete "slug" input from My/BackendBundle/Form/PostType.php.
I don't know if I'm doing something wrong or it's just works like that? Sluggable behavior works good when I'm adding entity manually (by creating new Post() with title and text and doing $manager->flush()).
My /app/config/config.yml file:
imports:
- { resource: parameters.yml }
- { resource: security.yml }
framework:
#esi: ~
translator: { fallback: %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_proxies: ~
session: ~
fragments: ~
# 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, add the path in parameters.yml
# e.g. database_path: %kernel.root_dir%/data/data.db3
# path: %database_path%
orm:
auto_generate_proxy_classes: %kernel.debug%
auto_mapping: true
mappings:
StofDoctrineExtensionsBundle: false
stof_doctrine_extensions:
default_locale: en_US
orm:
default:
sluggable: true
# Swiftmailer Configuration
swiftmailer:
transport: %mailer_transport%
host: %mailer_host%
username: %mailer_user%
password: %mailer_password%
spool: { type: memory }
fos_user:
db_driver: orm
firewall_name: main
user_class: My\UserBundle\Entity\User
If it's normal and I have to do it manually simple confirmation would be great.
app/console doctrine:generate:crud command use generate:doctrine:form command which generate fields for all entity properties.
So yes, you must remove unnecessary fields from form OR use more advenced generators like AdmigeneratorGeneratorBundle.
I have 2 bundles in my project:
src/Korea/AlmacenBundle
src/Galvez/RepuestosBundle
Each with their own database
korea_motos -> AlmacenBundle
galvez_motos -> RepuestosBundle
Actually my security.yml has only one provider:
providers:
korea:
entity: { class: Korea\AlmacenBundle\Entity\Usuario, property: username }
As you can see, both bundles are authenticated by the same table: Usuario, in korea_motos
TABLE: Usuario (korea_motos database)
--ID--|----USERNAME----|---------BUNDLE---
-----1-----|-------------admin----------------|----------AlmacenBundle----------
-----2-----|-------------admin----------------|----------RepuestosBundle-------
Now i want to validate the users, for RepuestosBundle with a table Usuario in galvez_motos, removing the column "bundle" in the previous table.
The problem is in the security.yml file. If i make this:
providers:
korea:
entity: { class: Korea\AlmacenBundle\Entity\Usuario, property: username }
galvez:
entity: { class: Galvez\RepuestosBundle\Entity\Usuario, property: username }
Symfony launch a exception:
The class 'Galvez\RepuestosBundle\Entity\Usuario' was not found in the chain configured namespaces Korea\AlmacenBundle\Entity
Im trying to use 2 providers, one table per each bundle.. is this possible?
Files:
security.yml
jms_security_extra:
secure_all_services: false
expressions: true
security:
encoders:
Korea\AlmacenBundle\Entity\Usuario:
algorithm: sha1
encode_as_base64: false
iterations: 1
Galvez\RepuestosBundle\Entity\Usuario:
algorithm: sha1
encode_as_base64: false
iterations: 1
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH ]
providers:
korea:
entity: { class: Korea\AlmacenBundle\Entity\Usuario, property: username }
galvez:
entity: { class: Galvez\RepuestosBundle\Entity\Usuario, property: username }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/demo/secured/login$
security: false
secured_area:
pattern: ^/
anonymous: ~
access_denied_handler: accessdenied_handler
form_login:
login_path: /login
check_path: /login_check
default_target_path: /redirect
always_use_default_target_path: true
logout:
path: /logout
target: /login
#anonymous: ~
#http_basic:
# realm: "Secured Demo Area"
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/redirect, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/galvez, roles: ROLE_ADMIN_GALVEZ }
- { path: ^/, roles: ROLE_ADMIN_KOREA }
config.yml -- can't copy/paste all :(
doctrine:
dbal:
default_connection: default
connections:
default:
driver: "%database_driver%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
host: "%database_host%"
port: "%database_port%"
charset: UTF8
galvez:
driver: %database_driver%
dbname: %database_name2%
user: %database_user2%
password: %database_password2%
host: %database_host%
port: %database_port%
charset: UTF8
orm:
default_entity_manager: default
entity_managers:
default:
connection: default
mappings:
AlmacenBundle: ~
galvez:
connection: galvez
mappings:
RepuestosBundle: ~
parameters.yml
parameters:
database_driver: pdo_mysql
database_host: localhost
database_port: null
database_name: korea_motos
database_user: root
database_password:
mailer_transport: smtp
mailer_host: localhost
mailer_user: null
mailer_password: null
locale: en
secret: 5f7ac4e7c2b38d6dbe55a1f05bee2b02
database_path: null
database_name2: galvez_motos
database_user2: root
database_password2:
PD: Sry for my english :S
Old question but for anyone looking for a solution, the manual explains it all here.
Basically, you need to chain your providers like this:
# app/config/security.yml
security:
providers:
chain_provider:
chain:
providers: [korea, galvez]
korea:
entity: { class: Korea\AlmacenBundle\Entity\Usuario, property: username }
galvez:
entity: { class: Galvez\RepuestosBundle\Entity\Usuario, property: username }
This is probably an issue with namespaces of your classes. Check if class Galvez\RepuestosBundle\Entity\Usuario is in the right namespace and if configuration is correct - maybe you accidentally left some copy-and-paste code from the other entity.
Try to persist both of these entities and fetch them (without security context) - I think you'll find your issue there.
I made a test with both entities:
abcController.php
$em= $this->get('doctrine')->getManager('galvez');
$usuario_g = $this->get('doctrine')->getRepository('RepuestosBundle:Usuario', 'galvez')->find(1);
$usuario_g->setUsername('asdasd');
$em->persist($usuario_g);
$em->flush();
Works fine, but if i use
$em = $this->getDoctrine()->getEntityManager();
instead of
$em = $this->get('doctrine')->getManager('galvez');
When i try to flush, Symfony launchs the same error:
The class 'Galvez\RepuestosBundle\Entity\Usuario' was not found in the chain configured namespaces Korea\AlmacenBundle\Entity
Usuario.php (AlmacenBundle)
<?php
namespace Korea\AlmacenBundle\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* Usuario
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Korea\AlmacenBundle\Entity\UsuarioRepository")
*/
class Usuario implements UserInterface
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="username", type="string", length=255)
*/
private $username;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=255)
*/
private $password;
/**
* #var string
*
* #ORM\Column(name="salt", type="string", length=255)
*/
private $salt;
Usuario.php (RepuestosBundle)
<?php
namespace Galvez\RepuestosBundle\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* Usuario
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Galvez\RepuestosBundle\Entity\UsuarioRepository")
*/
class Usuario implements UserInterface
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="username", type="string", length=255)
*/
private $username;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=255)
*/
private $password;
/**
* #var string
*
* #ORM\Column(name="salt", type="string", length=255)
*/
private $salt;
PD: Well, i think it's not possible, if i change this:
default_connection: korea
to:
default_connection: galvez
Symfony says:
MappingException: The class 'Korea\AlmacenBundle\Entity\Usuario' was not found in the chain configured namespaces Galvez\RepuestosBundle\Entity
the same error, but in reverse..
It seems like the monolog validation take the default connection (korea in this case) for search and validate