config
config.yml
doctrine:
dbal:
default_connection: default
connections:
default:
driver: pdo_mysql
host: '%database_host%'
port: '%database_port%'
dbname: '%database_name%'
user: '%database_user%'
password: '%database_password%'
charset: UTF8
customer:
driver: pdo_mysql
host: '%database_host%'
port: '%database_port%'
dbname: 'trade'
user: '%database_user%'
password: '%database_password%'
charset: UTF8
orm:
default_entity_manager: default
entity_managers:
default:
connection: default
mappings:
AppBundle: ~
customer:
connection: customer
mappings:
AppBundle:
dir: EntityTrade
AppBundle\Entity\acclogin
class acclogin
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string",unique=true)
* #Assert\NotBlank(message="can't empty!",groups={"registration","login"})
* #Assert\Length(min=6,max=64,minMessage="invalid 1",maxMessage="invalid 1",groups={"registration","login"})
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="AppBundle\EntityTrade\PayOrder",mappedBy="account")
*/
private $payorders;
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return account_login
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
}
//AppBundle\EntityTrade\PayOrder
class PayOrder
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string")
* #Assert\NotBlank(message="can't empty!",groups={"payAdd"})
*/
private $pay_id;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\acclogin",inversedBy="payorders")
* #ORM\JoinColumn(name="pay_id",referencedColumnName="name")
*/
private $account;
.....
....
...........here is getter and setter
}
//AppBundle\EntityTrade\PayOrderRepository
class PayOrderRepository extends \Doctrine\ORM\EntityRepository {
public function get( PayOrder $payOrder ) {
$pay_no = $payOrder->getPayNo();
return $this->findOneBy( [ 'pay_no' => $pay_no ] );
}
}
it throw a exception which is "Missing value for primary key id on AppBundle\Entity\acclogin" at this code "$this->findOneBy( [ 'pay_no' => $pay_no ] );"
why?am i have to add a primary key on the "name",any body can help me?thank
you very much !
if i change the #ORM\JoinColumn(name="pay_id",referencedColumnName="name") to #ORM\JoinColumn(name="id",referencedColumnName="id"), i will work ,but $PayOrder->getAccount(),only the id has the data ,the name of acclogin is always null 。
Doctrine requires you to use the primary key on the join columns.
See the documentation for JoinColumn() here:
https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/annotations-reference.html#annref_joincolumn
Particularly this line:
referencedColumnName: Name of the primary key identifier that is used
for joining of this relation.
Related
I have this error when i try to do http://localhost:8000/profile or any search :
(1/1) FatalErrorException
Compile Error: Access level to AppBundle\Entity\User::$id must be protected (as in class FOS\UserBundle\Model\User) or weaker
in User.php line 19
I just want to create a webpage for admin for my application (that webpage will be like a dashboard) and i don't know how to set access to this webpages for only admin so i tried with FOSUser with a login but if there is a quicker method just tell me (i tried the method : https://symfony.com/doc/current/security/form_login_setup.html but that don't work to it just reload the page when i click submit.)
pls help
My 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
image_directory: '%kernel.project_dir%/web/uploads/images'
framework:
#esi: ~
translator: { fallbacks: ['%locale%'] }
secret: '%secret%'
router:
resource: '%kernel.project_dir%/app/config/routing.yml'
strict_requirements: ~
form: ~
csrf_protection: ~
validation: { enable_annotations: true }
#serializer: { enable_annotations: true }
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 }
fos_user:
registration:
form:
type: AppBundle\Form\RegistrationType
db_driver: orm # other valid values are 'mongodb' and 'couchdb'
firewall_name: main
user_class: AppBundle\Entity\User
service: # this lines
mailer: fos_user.mailer.twig_swift # this lines
from_email:
address: "testapp#testapp.com"
sender_name: "Test App"
My security file :
# app/config/security.yml
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_token_generator: security.csrf.token_manager
default_target_path: /list
always_use_default_target_path : true
logout: true
anonymous: true
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
And that is my user entity :
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
use FOS\UserBundle\Model\User as BaseUser;
/**
* User
*
* #ORM\Table(name="user", uniqueConstraints=
{#ORM\UniqueConstraint(name="user_id_uindex", columns={"id"}),
#ORM\UniqueConstraint(name="user_username_uindex", columns=
{"username"})}, indexes={#ORM\Index(name="user_profile_id_fk",
columns={"id_profile"}), #ORM\Index(name="user_localisation_id_fk",
columns={"id_location"})})
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
* #UniqueEntity("username", groups={"Default", "Patch"})
*/
class User extends BaseUser
{
const ROLE_USER = 'ROLE_USER';
const ROLE_ADMIN = 'ROLE_ADMIN';
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
* #Serializer\Groups({"Default", "Deserialize", "user_detail"})
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="username", type="string", length=32, nullable=false)
* #Serializer\Groups({"Default", "Deserialize", "user_detail"})
*/
private $username;
/**
* #var string
* #Serializer\Type("string")
* #Assert\Expression(
* "this.getPassword() === this.getRetypedPassword()",
* message="Password does not match",
* groups={"Default", "Patch"}
* )
* #Assert\NotBlank(groups={"Default"})
* #Serializer\Groups({"Deserialize"})
*/
private $retypedPassword;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=255, nullable=false)
* #Serializer\Groups({"Deserialize", "user_detail"})
* #Assert\Regex(
* pattern="/(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{7,}/",
* message="Password must be seven characters long and contain at least one digit code, upper case, and lower case letter!",
* groups={"Default", "Patch"}
* )
*/
private $password;
/**
* #var boolean
*
* #ORM\Column(name="enabled", type="boolean", nullable=false)
*/
private $enabled = '1';
/**
* #var \AppBundle\Entity\Localisation
*
* #ORM\ManyToOne(targetEntity="Localisation")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="id_location", referencedColumnName="id")
* })
*/
private $idLocation;
/**
* #var \AppBundle\Entity\Profile
*
* #ORM\ManyToOne(targetEntity="Profile")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="id_profile", referencedColumnName="id")
* })
*/
private $idProfile;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="User", inversedBy="idUserOne")
* #ORM\JoinTable(name="friend",
* joinColumns={
* #ORM\JoinColumn(name="id_user_one", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="id_user_two", referencedColumnName="id")
* }
* )
*/
private $idUserTwo;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Place", inversedBy="idUserInvited")
* #ORM\JoinTable(name="list_invited",
* joinColumns={
* #ORM\JoinColumn(name="id_user_invited", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="id_place_invited", referencedColumnName="id")
* }
* )
*/
private $idPlaceInvited;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Place", mappedBy="idUserPresent")
*/
private $idPlacePresent;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Place", inversedBy="idUserPlace")
* #ORM\JoinTable(name="user_place",
* joinColumns={
* #ORM\JoinColumn(name="id_user_place", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="id_place_place", referencedColumnName="id")
* }
* )
*/
private $idPlacePlace;
/**
* #var array
* #ORM\Column(type="simple_array", length=200)
* #Serializer\Exclude()
*/
private $roles;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->idUserTwo = new \Doctrine\Common\Collections\ArrayCollection();
$this->idPlaceInvited = new \Doctrine\Common\Collections\ArrayCollection();
$this->idPlacePresent = new \Doctrine\Common\Collections\ArrayCollection();
$this->idPlacePlace = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* #return int
*/
public function getId(): int
{
return $this->id;
}
/**
* #param int $id
*/
public function setId(int $id): void
{
$this->id = $id;
}
/**
* #return string
*/
public function getUsername(): string
{
return $this->username;
}
/**
* #param string $username
*/
public function setUsername(string $username): void
{
$this->username = $username;
}
/**
* #return string
*/
public function getPassword(): string
{
return $this->password;
}
/**
* #param string $password
*/
public function setPassword(string $password): void
{
$this->password = $password;
}
/**
* #return bool
*/
public function isEnabled(): bool
{
return $this->enabled;
}
/**
* #param bool $enabled
*/
public function setEnabled(bool $enabled): void
{
$this->enabled = $enabled;
}
/**
* #return mixed
*/
public function getIdLocation()
{
return $this->idLocation;
}
/**
* #param mixed $idLocation
*/
public function setIdLocation($idLocation): void
{
$this->idLocation = $idLocation;
}
/**
* #return mixed
*/
public function getIdProfile()
{
return $this->idProfile;
}
/**
* #param mixed $idProfile
*/
public function setIdProfile($idProfile): void
{
$this->idProfile = $idProfile;
}
/**
* #return \Doctrine\Common\Collections\Collection
*/
public function getIdUserTwo(): \Doctrine\Common\Collections\Collection
{
return $this->idUserTwo;
}
/**
* #param \Doctrine\Common\Collections\Collection $idUserTwo
*/
public function setIdUserTwo(\Doctrine\Common\Collections\Collection $idUserTwo): void
{
$this->idUserTwo = $idUserTwo;
}
/**
* #return \Doctrine\Common\Collections\Collection
*/
public function getIdPlaceInvited(): \Doctrine\Common\Collections\Collection
{
return $this->idPlaceInvited;
}
/**
* #param \Doctrine\Common\Collections\Collection $idPlaceInvited
*/
public function setIdPlaceInvited(\Doctrine\Common\Collections\Collection $idPlaceInvited): void
{
$this->idPlaceInvited = $idPlaceInvited;
}
/**
* #return \Doctrine\Common\Collections\Collection
*/
public function getIdPlacePresent(): \Doctrine\Common\Collections\Collection
{
return $this->idPlacePresent;
}
/**
* #param \Doctrine\Common\Collections\Collection $idPlacePresent
*/
public function setIdPlacePresent(\Doctrine\Common\Collections\Collection $idPlacePresent): void
{
$this->idPlacePresent = $idPlacePresent;
}
/**
* #return \Doctrine\Common\Collections\Collection
*/
public function getIdPlacePlace(): \Doctrine\Common\Collections\Collection
{
return $this->idPlacePlace;
}
/**
* #param \Doctrine\Common\Collections\Collection $idPlacePlace
*/
public function setIdPlacePlace(\Doctrine\Common\Collections\Collection $idPlacePlace): void
{
$this->idPlacePlace = $idPlacePlace;
}
/**
* #return string
*/
public function getRetypedPassword(): string
{
return $this->retypedPassword;
}
/**
* #param string $retypedPassword
*/
public function setRetypedPassword(string $retypedPassword): void
{
$this->retypedPassword = $retypedPassword;
}
/**
* 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 $this->roles;
}
/**
* #param array $roles
*/
public function setRoles(array $roles)
{
$this->roles = $roles;
}
/**
* 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.
}
/**
* 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.
}
}
I use personal translation of StofDoctrineExtensionsBundle.
I've configured my App but i can't retrieve the translated labels, i get always the default text.
config.yml
# Doctrine Configuration
doctrine:
orm:
auto_generate_proxy_classes: "%kernel.debug%"
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
mappings:
menu_tree:
type: annotation
prefix: Gedmo\Tree\Entity
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity"
alias: MenuTree
is_bundle: false
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
orm:
default:
tree: true
translatable: true
sluggable: true
Then i wrote my personal Entity, this is a MenuItem
<?php
namespace App\Entity\Menu;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
/**
*
* #ORM\Table(name="mnu_item")
* #ORM\Entity(repositoryClass="App\Repository\Menu\MenuItem")
* #Gedmo\Tree(type="nested")
* #Gedmo\TranslationEntity(class="App\Entity\Menu\MenuItemTranslation")
*/
class MenuItem{
/**
*
* #var integer
*
* #ORM\Column(name="id", type="integer", options={"unsigned"=true})
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*
* #var string
* #Gedmo\Translatable
* #ORM\Column(name="label", type="string", length=255, nullable=true)
*/
private $label;
/**
* #Gedmo\Locale
* Used locale to override Translation listener`s locale
* this is not a mapped field of entity metadata, just a simple property
* and it is not necessary because globally locale can be set in listener
*/
private $locale;
/**
* #ORM\OneToMany(targetEntity="\App\Entity\Menu\MenuItemTranslation",
* mappedBy="object",
* cascade={"persist", "remove"})
*/
private $translations;
/**
* #var \App\Entity\Menu\Menu
*
* #ORM\ManyToOne(targetEntity="App\Entity\Menu\Menu", inversedBy="menuItems")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="menu_id", referencedColumnName="id", onDelete="CASCADE")
* })
*/
private $menu;
/**
* Constructor
*/
public function __construct() {
$this->ruoli = new \Doctrine\Common\Collections\ArrayCollection();
$this->children = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/**
* Set label
*
* #param string $label
*
* #return MenuItem
*/
public function setLabel($label) {
$this->label = $label;
return $this;
}
/**
* Get label
*
* #return string
*/
public function getLabel() {
return $this->label;
}
/**
* Set menu
*
* #param \App\Entity\Menu\Menu $menu
*
* #return MenuItem
*/
public function setMenu(\App\Entity\Menu\Menu $menu = null) {
$this->menu = $menu;
return $this;
}
/**
* Get menu
*
* #return \App\Entity\Menu\Menu
*/
public function getMenu() {
return $this->menu;
}
/**
*
* #return type
*/
public function getTranslations(){
return $this->translations;
}
/**
*
* #param \App\Entity\Menu\MenuItemTranslation $t
*/
public function addTranslation(MenuItemTranslation $t){
if (!$this->translations->contains($t)) {
$this->translations[] = $t;
$t->setObject($this);
}
}
public function setTranslatableLocale($locale){
$this->locale = $locale;
}
}
At least i have my Translator Class
<?php
namespace App\Entity\Menu;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation;
/**
* Description of MenuItemTranslation
/*
* #ORM\Entity
* #ORM\Table(name="mnu_menu_item_translations",
* uniqueConstraints={#ORM\UniqueConstraint(name="lookup_unique_idx", columns={
* "locale", "object_id", "field"
* })}
* )
*/
class MenuItemTranslation extends AbstractPersonalTranslation {
/**
* Convenient constructor
*
* #param string $locale
* #param string $field
* #param string $value
*/
public function __construct($locale, $field, $value)
{
$this->setLocale($locale);
$this->setField($field);
$this->setContent($value);
}
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Menu\MenuItem", inversedBy="translations")
* #ORM\JoinColumn(name="object_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $object;
}
I have translated my label and it works, but in a twig tempate, using item.label or item.getLabel() i obtain always the default MenuItem valu (E.g. Test insted of Prova, see images)
Menu Item
Menu Item Translation
I've messed with locale.
I changed the stof config 'cos my site is all in english and i need italian translation
stof_doctrine_extensions:
default_locale: "%locale%" #this is my error, just remove this line
# to set it back to en_US (default value).
# This indicates the locale of original table,
# if it's set to the same
# locale of the entire system it won't
# retrieve any translation
translation_fallback: true
orm:
default:
tree: true
translatable: true
sluggable: true
So the correct one is
Then i changed the stof config 'cos my site is all in english and i need italian translation
stof_doctrine_extensions:
translation_fallback: true
orm:
default:
tree: true
translatable: true
sluggable: true
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 a problem with the FOSUserBundle.
A know problem : 'Unrecognized field: usernameCanonical for symfony2 FosUserbundle' when I try a login
AND
on schema update I get this error:
Duplicate definition of column 'username' on entity 'Acme\ProjectBundle\Entity\User' in a field or discriminator column mapping.
I get this error ONLY IF I add the 'FOSUserBundle: ~' to settings of doctrine's mapping in the config.yml
I have tried a lot of solutions but I don't have resoved my problem :/
Please help me.
I have followed the FOS' team: https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/index.md
Before everything worked perfectly ...
I have Symfony 2.1.9
My config.yml:
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
service:
driver: "%database_driver2%"
host: "%database_host2%"
port: "%database_port2%"
dbname: "%database_name2%"
user: "%database_user2%"
password: "%database_password2%"
charset: UTF8
orm:
auto_generate_proxy_classes: "%kernel.debug%"
#auto_mapping: true
default_entity_manager: default
entity_managers:
default:
metadata_cache_driver: apc
result_cache_driver: apc
query_cache_driver: apc
connection: default
mappings:
FOSUserBundle: ~
AcmeProjectBundle: {type: yml, dir: Resources/config/doctrine/ } #also tried wit '~'
fos_user:
db_driver: orm # other valid values are 'mongodb', 'couchdb' and 'propel'
firewall_name: main
user_class: Acme\ProjectBundle\Entity\User
My Acme\ProjectBundle\Entity\User.php:
namespace Acme\ProjectBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Security\Core\User\UserInterface;
class User extends BaseUser implements UserInterface, \Serializable
{
const TYPE_ADMIN = 0;
const TYPE_USER = 2;
const TYPE_ARTIST = 3;
/**
* #var string $salt
*/
protected $salt;
/**
* #var boolean $is_active
*/
private $is_active;
protected $id;
private $name;
protected $username;
protected $email;
/**
* #var tinyint $type
*/
private $type;
protected $password;
private $description;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* 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 type
*
* #param integer $type
* #return User
*/
public function setType($type)
{
$this->type = $type;
return $this;
}
/**
* Get type
*
* #return integer
*/
public function getType()
{
return $this->type;
}
/**
* Set description
*
* #param string $description
* #return User
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set username
*
* #param string $username
* #return User
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Get username
*
* #return string
*/
public function getUsername()
{
return $this->username;
}
/**
* Set password
*
* #param string $password
* #return User
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Get password
*
* #return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Set email
*
* #param string $email
* #return User
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* #return string
*/
public function getEmail()
{
return $this->email;
}
public function isPasswordName()
{
return ($this->name != $this->password);
}
public function isPassUsername()
{
return ($this->password != $this->username);
}
/**
* #var \DateTime $date
*/
private $date;
/**
* Set date
*
* #param \DateTime $date
* #return User
*/
public function setDate($date)
{
$this->date = $date;
return $this;
}
/**
* Get date
*
* #return \DateTime
*/
public function getDate()
{
return $this->date;
}
private $updateDate;
/**
* Set updateDate
*
* #param \DateTime $updateDate
* #return User
*/
public function setUpdateDate($updateDate)
{
$this->updateDate = $updateDate;
return $this;
}
/**
* Get updateDate
*
* #return \DateTime
*/
public function getUpdateDate()
{
return $this->updateDate;
}
public function setIsActive($value)
{
$this->is_active = $value;
return $this;
}
public function gettIsActive()
{
return $this->is_active;
return $this;
}
/**
* Set salt
*
* #param string $salt
* #return User
*/
public function setSalt($salt)
{
$this->salt = $salt;
return $this;
}
/**
* Get salt
*
* #return string
*/
public function getSalt()
{
return $this->salt;
}
/**
* #inheritDoc
*/
public function eraseCredentials()
{
}
public function __construct()
{
parent::__construct();
$this->isActive = true;
$this->salt = md5(uniqid(null, true));
}
public function getRoles()
{
switch ($this->getType())
{
case 0:
return array('ROLE_ADMIN');
break;
case 1:
case 2:
case 3:
return array('ROLE_USER');
break;
}
}
/**
* #see \Serializable::serialize()
*/
public function serialize()
{
return serialize(array(
$this->id,
));
}
/**
* #see \Serializable::unserialize()
*/
public function unserialize($serialized)
{
list (
$this->id,
) = unserialize($serialized);
}
/**
* #var integer $first_login
*/
private $first_login;
/**
* Get is_active
*
* #return boolean
*/
public function getIsActive()
{
return $this->is_active;
}
/**
* Set first_login
*
* #param integer $firstLogin
* #return User
*/
public function setFirstLogin($firstLogin)
{
$this->first_login = $firstLogin;
return $this;
}
/**
* Get first_login
*
* #return integer
*/
public function getFirstLogin()
{
return $this->first_login;
}
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*/
private $userPoints;
/**
* #var integer $privacy
*/
private $privacy;
/**
* Set privacy
*
* #param integer $privacy
* #return User
*/
public function setPrivacy($privacy)
{
$this->privacy = $privacy;
return $this;
}
/**
* Get privacy
*
* #return integer
*/
public function getPrivacy()
{
return $this->privacy;
}
/**
* #var integer
*/
private $enable;
/**
* Set enable
*
* #param integer $enable
* #return User
*/
public function setEnable($enable)
{
$this->enable = $enable;
return $this;
}
/**
* Get enable
*
* #return integer
*/
public function getEnable()
{
return $this->enable;
}
}
My security.yml
security:
firewalls:
main:
pattern: ^/
form_login:
provider: fos_userbundle
login_path: /login
use_forward: false
check_path: /login_check
csrf_provider: form.csrf_provider
logout: true
anonymous: true
providers:
fos_userbundle:
id: fos_user.user_provider.username
encoders:
FOS\UserBundle\Model\UserInterface: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
It seems your version of the FOSUserBundle is not good ... I had the same problem with a 1.3.* that I solved just changing this to version "~2.0#dev".
You can check this looking at your "fos_user" table ; if it just contains a single "id" field, your User entity is not extending the right "FOS\Entity\User" object ... try to upgrade your required version of the bundle in your "composer.json" and then rebuild your tables (below a full HARD rebuild - data are lost):
php app/console doctrine:schema:drop --force
php app/console doctrine:schema:create
If your "fos_user" table have all required fields, then you're good.
Review the docs on creating a use class: https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/index.md
Your User class should not be repeating all the properties (username etc) from the base class. It should only have the new properties like name and id.
And while you didn't show your doctrine mapping file, I'm guessing your probably duplicated everything in there is well? Only map the new properties.
In fact, i guess, you should have some piece of code on config.yml as
fos_user:
db_driver: orm # other valid values are 'mongodb', 'couchdb' and 'propel'
firewall_name: main
user_class: Acme\ProjectBundle\Entity\User
and you should have no need to add sth. to orm settings for FOSUser.
orm:
auto_generate_proxy_classes: "%kernel.debug%"
#auto_mapping: true
default_entity_manager: default
entity_managers:
default:
// that's for APCu or APC
metadata_cache_driver: apc
result_cache_driver: apc
query_cache_driver: apc
in dev env APC disabled, that's why you get this error. You have to comment it for dev or enable cacheClassLoader
I had this trouble and applied PieroWbmstr's suggestion, and started using 2.0 instead of 1.3.6... with doctrine.orm.auto_mapping set to true in config.yml
Once I made the composer.json version switch and upgraded, my doctrine:schema:update instantly recognised the new fields as missing in the current database instance, and applied them.
The newer version of the user class within FOS/UserBundle doesn't seem to have any significant changes that would force the mapping to play nicely. Does anyone have an idea as to what the difference is in these two versions? Or more directly, why the older version somehow didn't let doctrine recognise it's xml mappings (hint: in both of my FOS/UserBundle versions, I had my local custom bundles set to use annotations).
Thanks