symfony 2.8 generated crud controller with errors - symfony

I try generate CRUD controllers for my entities.
For example I wanna generate CRUD controller for AppBundle\Entity\User\User:
namespace AppBundle\Entity\User;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\Group;
/**
* #ORM\Entity
* #ORM\Table(name="user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
public function __construct()
{
parent::__construct();
// your own logic
}
}
Generate entities:
$ app/console generate:doctrine:entities AppBundle
Generate crud:
$ app/console doctrine:generate:crud --entity=AppBundle:User\User
This command generete follow controller:
class UserController extends Controller
{
/**
* Lists all User\User entities.
*
* #Route("/", name="user_user_index")
* #Method("GET")
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$user\Users = $em->getRepository('AppBundle:User\User')->findAll();
return $this->render('user/user/index.html.twig', array(
'user\Users' => $user\Users,
));
}
/**
* Finds and displays a User\User entity.
*
* #Route("/{id}", name="user_user_show")
* #Method("GET")
*/
public function showAction(User $user\User)
{
return $this->render('user/user/show.html.twig', array(
'user\User' => $user\User,
));
}
}
What the $user\Users? Symfony 2.8!
Maybe I can't use more directories in the Entity folder?

if you used the same namespace in your CRUD generation command as you have in this question, I expect that symfony is getting confused.
you have used:
AppBundle\Entity\User\User
note the extra \User.
If this isnt a typo, your entity should reside in the base Entity directory. The unusual path has probably confused it.
One would have thought however, that the generate command should have validated the string first.

Related

Persist twice on faker with nexest version

I have a problem on fixtures with fzaninotto/faker on a symfony project, i have some table with relations ManyToOne who not complete automaticaly when I load fixtures since i have update bundles on composer.
There is an error and a message who tell me to add cascad persist but if i do that the entry is create twice. The is my code and my error message, sorry for my bad english...
Project entity:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #ORM\Table(name="`user`");
*/
class User implements UserInterface, \Serializable
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=100, nullable=true)
*/
private $firstname;
/**
* #ORM\Column(type="string", length=100, nullable=true)
*/
private $lastname;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Role")
* #ORM\JoinColumn(nullable=false)
*/
private $role;
Role Entity:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\RoleRepository")
*/
class Role
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=100, unique=true)
*/
private $name;
/**
* #ORM\Column(type="string", length=100, unique=true)
*/
private $code;
/**
* #ORM\OneToMany(targetEntity="App\Entity\User", mappedBy="role")
*/
private $users;
Fixtures file:
<?php
namespace App\DataFixtures;
use Faker\Factory;
use App\Entity\Tag;
use App\Entity\Role;
use App\Entity\User;
use App\Entity\Skill;
use App\Entity\Follow;
use App\Entity\Statut;
use App\Entity\Techno;
use App\Entity\Comment;
use App\Entity\Project;
use App\Entity\Request;
use App\DataFixtures\Provider;
use Faker\ORM\Doctrine\Populator;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager)
{
$generator = Factory::create('fr_FR');
// Ajout provider custom Provider
$generator->addProvider(new Provider($generator));
$populator = new Populator($generator, $manager);
// REMPLIT LES TABLES SIMPLES
// table "role"
$populator->addEntity(
Role::class,
2,
[
'name' => function () use ($generator) {
return $generator->unique()->randomElement(['Administrateur', 'Utilisateur']);
}
]
);
// table "user"
$populator->addEntity(
User::class,
10,
[
'firstname' => function () use ($generator) {
return ($generator->firstName());
},
'lastname' => function () use ($generator) {
return ($generator->lastName());
},
]
);
And my error message when i load fixtures:
In ORMInvalidArgumentException.php line 105:
Multiple non-persisted new entities were found through the given association graph:
* A new entity was found through the relationship 'App\Entity\User#role' that was not configured to cascade persist operations
for entity: Administrateur. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or conf
igure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}).
* A new entity was found through the relationship 'App\Entity\User#role' that was not configured to cascade persist operations
for entity: Utilisateur. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configu
re cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}).
Same issue with 1.9 version, so just downgrade :
1/ Remove faker folder in vendor.
2/ Change composer.json from
"fzaninotto/faker": "^1.9"
to
"fzaninotto/faker": "1.8"
3/ Run composer update
--> Breath now...

How to use the registration procedure of FOSUserBundle from a third controller

I have to persist an entity (let's call it Entity for simplicity) in the database that has to be referenced to a User handled with FOSUserBundle. To make this reference I have a column entity_table.userId.
When the new Entity is created, I have to:
Create the User through the registration procedure of FosUserBundle;
Get the ID of the new created User: [meta code] $userId = $get->newCreatedUserId();
Set this id in Entity: $entity->setUserId($userId);
Persist the Entity to the database.
How can I integrate the registration procedure of FosUserBundle into the controller that persists my Entity?
MORE DETAILS
In the first time I tried to simply copy the code from the method registerAction() of the RegistrationController of FOSUserBundle: a quick and dirty approach that, anyway didn't work as i get an error as the User class i passed was wrong (I passed my custom User entity I use to overwrite the bundle).
This kind of approach has also other drawbacks:
I cannot control the registration procedure (send or decide to not send confirmation e-mails, for example);
I cannot use the builtin checks on passed data;
I cannot be sure that on FOSUserBundles updates my custom method continue to work
Others I cannot imagine at the moment...
So, I'd like to create the user in the cleanest way possible: how can i do this? Which should be a good approach?
A controller forwarding?
Anyway, an "hardcoded" custom method that emulates the registerAction() method?
A custom registration form?
I have read a lot of discussions here at StackOverflow and on Internet, I read the documentation of FOSUserBundle and of Symfony too, but I cannot decide for the good approach, also because I'm not sure I have understood all the pros and cons of each method.
If someone can put me on the right way... :)
SOMETHING MORE ABOUT MY REGISTRATION FLOW
I have a getStarted procedure handled by the controller GetStarteController.
In it I have two methods:
indexAction(), that displays a registration form with only the field "email";
endAction(), that receive the form and creates a Company using the passed e-mail (it gets the domain part only of the email).
HERE IS A WORKING MESSY CODE (inside it for Companies and Stores are called some methods that exists in the source code but are not in the posted classes below, as setBrand() or setUrl(), for example).
// AppBundle/Controller/getStartedController.php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;
use MyVendor\UserBundle\Entity\User;
use AppBundle\Entity\Companies;
use AppBundle\Entity\Stores;
class GetStartedController extends Controller
{
/**
* #Route("getstarted")
* #Template()
*/
public function indexAction()
{
$data = array();
$form = $this->createFormBuilder($data, array(
'action' => $this->generateUrl('getStartedEnd'),
))
->add('email', 'email')
->add('submit', 'submit')
->getForm();
return array(
'form' => $form->createView(),
);
}
/**
* #Route("getstarted/end", name="getStartedEnd")
* #Template()
*/
public function endAction(Request $request)
{
$form = $this->createFormBuilder()
->add('email', 'email')
->add('submit', 'submit')
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
$data = $form->getData();
} else {
/** #todo here we have to raise some sort of exception or error */
echo 'no data submitted (See the todo in the code)';exit;
}
// Pass the email to the template
$return['email'] = $data['email'];
// Get the domain part of the email and pass it to the template
$domain = explode('#', $data['email']);
$return['domain'] = $domain[1];
// 1) Create the new user
$user = new User();
// Get the token generator
$tokenGenerator = $this->container->get('fos_user.util.token_generator');
$user->setEmail($return['email']);
$userRandomUsername = substr($tokenGenerator->generateToken(), 0, 12);
$user->setUsername('random-' . $userRandomUsername);
$plainPassword = substr($tokenGenerator->generateToken(), 0, 12);
$encoder = $this->container->get('security.password_encoder');
$encoded = $encoder->encodePassword($user, $plainPassword);
// Set the password for the user
$user->setPassword($encoded);
/** #var $userManager \FOS\UserBundle\Model\UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
// Perstist the user in the database
$userManager->updateUser($user);
$userId = $user->getId();
// 2) Create the Company object
$company = new Companies();
$company->setBrand($return['domain'])
->setAdded(new \DateTime())
->setOwnerId($userId);
// 3) Create the Store object
$store = new Stores();
$store->setEmail($return['email'])
->setUrl($return['domain'])
->setAdded(new \DateTime());
// Get the Entity Manager
$em = $this->getDoctrine()->getManager();
// Persist Company and get its ID
$em->persist($company);
$em->flush();
$return['companyId'] = $company->getId();
// Set the property branchOf of the Store object
$store->setBranchOf($return['companyId']);
// Persist the Store object
$em->persist($store);
$em->flush();
$return['storeId'] = $store->getId();
return $return;
}
}
Here the User Entity that ovewrites the one provided by FOSUserBundle
// MyVendor/UserBundle/Entity/User.php
namespace MyVendor\UserBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="prefix_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
public function __construct()
{
parent::__construct();
// your own logic
}
}
Some essential code of Companies.php
// AppBundle/Entity/Companies.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Companies
*
* #ORM\Table(name="companies")
* #ORM\Entity
*/
class Companies
{
/**
* #var integer
*
* #ORM\Column(name="ownerId", type="integer", nullable=false)
*/
private $ownerid;
/**
* Set ownerid
*
* #param integer $ownerid
* #return Companies
*/
public function setOwnerid($ownerid)
{
$this->ownerid = $ownerid;
return $this;
}
/**
* Get ownerid
*
* #return integer
*/
public function getOwnerid()
{
return $this->ownerid;
}
}
Some essential code of Stores.php
// AppBundle/Entity/Stores.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Stores
*
* #ORM\Table(name="stores", uniqueConstraints={#ORM\UniqueConstraint(name="branchOf", columns={"branchOf"})})
* #ORM\Entity
*/
class Stores
{
/**
* #var integer
*
* #ORM\Column(name="branchOf", type="integer", nullable=false)
*/
private $branchof;
/**
* Set branchof
*
* #param integer $branchof
* #return Stores
*/
public function setBranchof($branchof)
{
$this->branchof = $branchof;
return $this;
}
/**
* Get branchof
*
* #return integer
*/
public function getBranchof()
{
return $this->branchof;
}
}
You can use a custom registration form but the best way is clearly to listen to registration event dispatched by FOSUser.
Here is an example :
class RegistrationListener implements EventSubscriberInterface
{
/**
* L'entity manager
*
* #var EntityManager
*/
private $em;
/**
* Constructeur de l'EventListener
*
* #param \Doctrine\ORM\EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager)
{
$this->em = $entityManager;
}
/**
* {#inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::REGISTRATION_INITIALIZE => 'onRegistrationInit',
);
}
/**
* Triggered when FOSUserEvents::REGISTRATION_INITIALIZE is caught.
*
* #param \FOS\UserBundle\Event\UserEvent $userEvent
*/
public function onRegistrationInit(UserEvent $userEvent)
{
$user = $userEvent->getUser();
// Define your own logic there
}
}
Don't forget to make this listener a service:
#services.yml
services:
oe_user.registration:
class: OrienteExpress\UserBundle\EventListener\RegistrationListener
# arguments are optional but you still can need them
# so I let the EM as example which is an often used parameter
arguments:
entityManager: "#doctrine.orm.entity_manager"
tags:
- { name: kernel.event_subscriber }
You'll find the complete list of event dispatched by FOSUser here
Moreover, Symfony entities are a model of objects. That said, you need to understand that you don't work with ids within your model, but object.
You should not use thing such as $var->setUserId() within entites. Doctrine is there to manage your relations, so be carefull about this. You might face unexpected problem by not using Symfony & Doctrine the way it has been designed for.
EDIT:
In your company entity, your relation is beetween a Company and a User objects. That means you dont need a User id in your company but just a instance of User.
I think you might go back to the basics before wanting to do advanced stuff.
Your relation beetween the user and the company should not be designed by an integer attribute but a real doctrine relation.
Ex:
class Company {
/**
* #ORM\ManyToOne(targetEntity="Path\To\User")
* #ORM\JoinColumn(nullable=false)
*/
private $owner;
/**
* #param $user User
*/
public function setUser(User $user)
{
$this->user = $user;
}
}
Then when you'll create a new company. You won't need to know the User's id or even insert it to make the link between them. But if you are not aware yet of this, once again, I think you should go back to the basics of Symfony since this is one of the most (maybe the most) important feature to master.

How to access to Doctrine Document Manager from Doctrine Entity Repository

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

Symfony2 Relationships using Interfaces results in duplicated table

I'm trying to relate an entity in one bundle with another in another bundle to make the second one independent from the first one, and be able to reuse it.
I'm following this documentation and this StackOverflows answer.
In the reusable bundle I have a Folder, File a that belongs to the folder and and interface like this:
namespace Acme\FolderBundle\Entity;
/**
* #ORM\Entity
*/
class Folder implements FolderInterface
{
// Has many files
}
namespace Acme\FolderBundle\Entity;
interface FolderInterface
{
// no methods here
}
namespace Acme\FolderBundle\Entity;
/**
* #ORM\Entity
*/
class File
{
// Belongs to one folder
}
And on the other bundle just one class:
namespace Acme\NewBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Acme\FolderBundle\Entity\Folder as BaseFolder;
use Acme\FolderBundle\Entity\FolderInterface;
/**
* #ORM\Entity
*/
class Folder extends BaseFolder implements FolderInterface
{
// Has many files
}
And the config.yml's ORM configuration:
orm:
auto_generate_proxy_classes: %kernel.debug%
auto_mapping: true
resolve_target_entities:
Acme\FolderBundle\Entity\FolderInterface: Acme\NewBundle\Entity\Folder
If I try to update my database schema, I get the following error:
[Doctrine\DBAL\Schema\SchemaException]
The table with name 'foldersDatabase.folder' already exists.
To get this working, I have to explicitly change one of the Folder's Entities table:
namespace Acme\FolderBundle\Entity;
/**
* #ORM\Entity
* #ORM\Table(name="distributed_folder")
*/
class Folder implements FolderInterface
{
// Has many files
}
Then, everything works but I get stuck with a table in my database (distributed_folder) that is never used.
Thanks a lot in advance!!
EDIT:
Fixed the annotation in the FolderInterface
You can not make one entity extend another entity this way.
If you want to have an abstract class which contains the fields for two or more subclass entities, you should mark the abstract class as #ORM\MappedSuperclass , and make sure, it will not have the annotation #Entity. While on the subclasses , they each should have #Entity annotation , and #Table annotation with a unique name attribute.
Here is an example :
<?php
namespace Radsphere\MissionBundle\Model\Core\BaseAbstract;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\MappedSuperclass
*
* An abstract class implementation of mission
*/
abstract class AbstractMission implements MissionInterface, IntegratedPluginInterface
{
/**
* #ORM\Id()
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=36, unique=true)
*/
protected $guid;
/**
* #ORM\Column(type="string", length=255)
*/
protected $title;
/**
* #ORM\ManyToMany(targetEntity="MissionTask", cascade={"persist", "remove"})
* #ORM\JoinTable(name="mtm_mission_task",
* joinColumns={#ORM\JoinColumn(name="mission_id", referencedColumnName="id", onDelete="CASCADE")},
* inverseJoinColumns={#ORM\JoinColumn(name="task_id", referencedColumnName="id", onDelete="CASCADE")}
* )
*/
protected $tasks;
/**
* {#inheritDoc}
*/
public function addTask(MissionTaskInterface $missionTask)
{
$this->getTasks()->add($missionTask);
$missionTask->setMission($this);
}
/**
* {#inheritDoc}
*/
public function setTasks(Collection $tasks)
{
/** #var MissionTaskInterface $task */
foreach ($tasks as $task) {
$task->setMission($this);
}
$this->tasks = $tasks;
}
/**
* {#inheritDoc}
*/
public function getTasks()
{
$tasks = $this->tasks;
foreach ($tasks as $task) {
if ($task instanceof MissionTaskInterface) {
if (!$task->getIsEnabled()) {
/** #var $tasks Collection */
$tasks->removeElement($task);
}
}
}
return $tasks;
}
}
and the entity itself:
<?php
namespace Radsphere\MissionBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Radsphere\MissionBundle\Model\Core\BaseAbstract\AbstractMission;
/**
* Mission entity
*
* #ORM\Table(name="mission_bundle_mission", indexes={#ORM\Index(name="guid_idx", columns={"guid"})})
* #ORM\HasLifecycleCallbacks
* #ORM\Entity(repositoryClass="MissionRepository")
*/
class Mission extends AbstractMission
{
/**
* Constructor
*/
public function __construct()
{
$this->tasks = new ArrayCollection();
}
}

Symfony 2.3.6 generate CRUD panel from FOSuserbundle

I trying to create CRUD panel from FOSUserBundle but i have some troubles. I mean that i created User entity for FOS and made crud panel for this entity. Now when i trying to add new user i have error like below
Neither the property "expiresAt" nor one of the methods "getExpiresAt()", "isExpiresAt()", "hasExpiresAt()", "_get()" or "_call()" exist and have public access in class "Bn\UserBundle\Entity\User".
It's my first project so please understand when i will ask for simple function, some suggestion ? What is wrong ?
<?php
namespace Bn\UserBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* User
*
* #ORM\Table(name="fos_user")
* #ORM\Entity
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* Get expiresAt
*
* #return \DateTime
*/
public function getExpiresAt()
{
return $this->expiresAt;
}
/**
* Get credentials_expire_at
*
* #return \DateTime
*/
public function getCredentialsExpireAt()
{
return $this->credentialsExpireAt;
}
public function __construct()
{
parent::__construct();
// your own logic
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
}
Now is working but i don't know why i must declare again function for getter.
I believe this means you need to add public accessors setExpiresAt() and getExpiresAt() to your User entity.
You need only add getExpiresAt to your User.php class. FOSUserBundle\User doesn't have getter for this field, but Sensio generator creates views for all fields.
public function getExpiresAt()
{
return $this->expiresAt;
}

Resources