How to use DI in a fixture class - symfony

I want to create a fixture for categories, but I get the error
Could you elaborate on how di works in Symfony. The documentation didn't tell me much.
in config / services.yaml the standard code is already specified which seems to solve the di problem
class CategoryFixtures extends \Doctrine\Bundle\FixturesBundle\Fixture
{
private $category;
public function __construct(
\App\Entity\Category $category
) {
$this->category = $category;
}
public function load(\Doctrine\Persistence\ObjectManager $manager)
{
$this->addMainCategories($manager);
}
private function addMainCategories($manager) {
$mainCategoriesArray = ['111', '222', '333', '444', '555'];
foreach ($mainCategoriesArray as $categoryName) {
$this->category->setName($categoryName);
$manager->persist($this->category);
}
$manager->flush();
}
}
Error
Cannot autowire service "App\DataFixtures\CategoryFixtures": argument "$category" of method "__construct()" references class "App\Entity\Category" but no such service exists.

DI isn't really the issue here.
Untested code to create 5 Category entities:
use App\Entity\Category;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Persistence\ObjectManager;
// Creates Category entities Category0, Category1...
class CategoryFixtures extends AbstractFixture
{
private $mainCategoriesArray = ['111', '222', '333', '444', '555'];
public function load(ObjectManager $manager)
{
for ($i = 0; $i < 5; $i++) {
$name = 'Category' . $i;
$$name = new Category();
$$name->setName($this->mainCategoriesArray[$i]);
$manager->persist($$name);
}
$manager->flush();
}
}

Related

Symfony EasyAdmin3: Argument 1 passed must be an instance of App\Entity

I use easyadmin for Symfony (I am a beginner), I'm stuck on this problem:
Argument 1 passed to App\Entity\MyOrder::setCarrier() must be an instance of App\Entity\Carrier or null, int given, called in /Users/My/Sites/test/src/Controller/Admin/MyOrderCrudController.php
(line in code: $myorder->setCarrier(2);)
I have this problem for all field with an relation.
however, My Entity:
/**
* #ORM\ManyToOne(targetEntity=Delivery::class, inversedBy="myOrder")
*/
private $delivery;
...
public function getCarrier(): ?carrier
{
return $this->carrier;
}
public function setCarrier(?carrier $carrier): self
{
$this->carrier = $carrier;
return $this;
}
...
My CrudController:
namespace App\Controller\Admin;
use App\Entity\MyOrder;
use App\Entity\Carrier;
use Doctrine\ORM\EntityManagerInterface;
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Field\ArrayField;
use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField;
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
use EasyCorp\Bundle\EasyAdminBundle\Router\CrudUrlGenerator;
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
class MyOrderCrudController extends AbstractCrudController
{
private $entityManager;
private $adminUrlGenerator;
public function __construct(EntityManagerInterface $entityManager, AdminUrlGenerator $adminUrlGenerator)
{
$this->entityManager = $entityManager;
$this->adminUrlGenerator = $adminUrlGenerator;
}
public static function getEntityFqcn(): string
{
return MyOrder::class;
}
public function configureCrud(Crud $crud): Crud
{
return $crud->setDefaultSort(['id' => 'DESC']);
}
public function configureActions(Actions $actions): Actions
{
$updateDelivery = Action::new('updateDelivery', 'Delivery up', 'fas fa-truck')->linkToCrudAction('updateDelivery');
return $actions
->add('detail', $updateDelivery)
->add('index', 'detail');
}
public function updateDelivery(AdminContext $context)
{
$myorder = $context->getEntity()->getInstance();
$myorder->setCarrier(2);
$this->entityManager->flush();
$url = $this->adminUrlGenerator->setRoute('admin', [])->generateUrl();
return $this->redirect($url);
}
setCarrier only accept a Carrier object. You can't pass "2" (I suppose it's the carrier id).
Try this :
$carrier = $this->entityManager->find(Carrier::class, 2);
$myorder->setCarrier($carrier);
PS : There's a typo in your entity (a class name has the first letter in uppercase, so "Carrier" instead of "carrier")

Do Subscribers work while loading Fixtures in Symfony?

I tried to run the fixture below on Symfony 5 using the command php bin/console d:f:l.
I get this error: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'contact_email' cannot be null
The same code logic is working fine for Post entities when creating them manually through the CRUD. Are fixtures not compatible with subscribers (events) or did i make a mistake?
Thank you.
Edit: I'm also using EasyAdmin Bundle 3.
App\DataFixtures.php\AppFixtures.php
<?php
namespace App\DataFixtures;
use App\Entity\User;
use App\Entity\Author;
use Doctrine\Persistence\ObjectManager;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
class AppFixtures extends Fixture
{
/** #var User[] */
private $users = [];
/** #var Author[] */
private $authors = [];
/** #var UserPasswordHasherInterface */
private $hasher;
public function __construct(UserPasswordHasherInterface $hasher)
{
$this->hasher = $hasher;
}
public function load(ObjectManager $manager): void
{
$this->createUsers();
foreach($this->users as $user) $manager->persist($user);
$this->createAuthors();
foreach($this->authors as $author) $manager->persist($author);
$manager->flush();
}
public function createUsers(): void
{
$admin = (new User)
->setUsername('admin')
->setEmail('admin#admin.com')
->setRoles(['ROLE_ADMIN'])
->setFirstname('Edouard')
->setLastname('Proust');
$admin->setPassword($this->hasher->hashPassword($admin, 'admin'));
$this->users[] = $admin;
}
public function createAuthors(): void
{
foreach($this->users as $user) {
if(in_array('ROLE_ADMIN', $user->getRoles())) {
$author = (new Author)
->setUser($user)
->setAvatar('#')
->setBio('Bio')
// The line i want to get rid of:
// ->setContactEmail($user->getEmail())
;
$this->authors[] = $author;
}
}
}
}
App\EventListener\AuthorSubscriber.php
<?php
namespace App\EventListener;
use App\Entity\Author;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityPersistedEvent;
class AuthorSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
BeforeEntityPersistedEvent::class => 'setContactEmail',
];
}
public function setContactEmail(BeforeEntityPersistedEvent $event)
{
/** #var Author */
$entity = $event->getEntityInstance();
if($entity instanceof Author) {
if(!$entity->getContactEmail()) {
$user = $entity->getUser();
$contactEmail = $user ? $user->getEmail() : '#';
$entity->setContactEmail($contactEmail);
}
}
}
}
EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityPersistedEvent:class is not proper Symfony event name. You probably should use Doctrine\ORM\Events::prePersist.
Also please check your DoctrineBundle version. If you're using the default services.yaml configuration and DoctrineBundle lower than 2.1, you have to configure services.yaml with:
App\EventListener\AuthorSubscriber:
tags:
- name: 'doctrine.event_subscriber'
You can read something more here: https://symfony.com/doc/current/doctrine/events.html#doctrine-lifecycle-subscribers

Extend Doctrine EntityRepository

I have written a class BasicRepository in order to use it instead of the EntityRepository to add some basic modification like remove all deleted-flaged items.
<?php
namespace AppBundle\Repository;
use AppBundle\DataFixtures\ORM\LoadEventPrioData;
use AppBundle\Entity\Location;
use Doctrine\ORM\EntityRepository;
class BasicRepository extends EntityRepository
{
public function createQueryBuilder($alias, $indexBy = null)
{
$query = parent::createQueryBuilder($alias);
dump(parent::getClassName());
dump($this->getClassName());
if (property_exists($this->getClassName(), 'isDeleted')) {
dump("Ping");
$query->andWhere($alias.'.isDeleted = :false')->setParameter('false', false);
}
else {
dump("Pong");
}
return $query;
}
}
Controller:
...
public function searchAction(Request $request) {
$em = $this->getDoctrine()->getManager();
$meta = new ClassMetadata('AppBundle:Location');
$er = new BasicRepository($em, $meta);
$query = $er->createQueryBuilder('u');
...
My aim is that - if the property "isDeleted" (boolean) exists in the Entity - the Query should contain an additional Where-Statement.
For some strange reason property_exists always return false - even when the property exits in the class.
I get your idea. The correct place you're looking for is Doctrine Filters. Check this package: https://github.com/DeprecatedPackages/DoctrineFilters#usage
There you can find example exactly with your use case:
<?php
use Doctrine\ORM\Mapping\ClassMetadata;
use Symplify\DoctrineFilters\Contract\Filter\FilterInterface;
final class SoftdeletableFilter implements FilterInterface
{
/**
* {#inheritdoc}
*/
public function addFilterConstraint(ClassMetadata $entity, $alias)
{
if ($entity->getReflectionClass()->hasProperty('isDeleted')) {
return "$alias.isDeleted = 0";
}
return '';
}
}

Error for load the fixtures Symfony 2 [OutOfBoundsException] Reference to: (image) does not exist

I have a OneToOne relation between the Article entity and Image entity, the Article entity is the owner , I created the fixtures data files to load the database , I used the " faker " to format the type file, when I run the command:
$ app / console doctrine : fixtures : load
I get this error message :
:[OutOfBoundsException]
Reference to: (image) does not exist
in my fixtures files:
ns\NikahBundle\DataFixtures\ORM\LoadArticleData.php:
<?php
namespace ns\NikahBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use ns\NikahBundle\Entity\Article;
class LoadArticleData extends AbstractFixture implements OrderedFixtureInterface
{
const MAX_NB_ARTICLES = 10;
public function load(ObjectManager $manager)
{
$faker = \Faker\Factory::create();
for ($i = 0; $i < self::MAX_NB_ARTICLES; ++$i) {
$article = new Article();
$article->setAuteur($faker->text(250));
$article->setTitre($faker->text(250));
$article->setContenu($faker->text(250));
$article->setDeleted($faker->boolean);
$image = $this->getReference('image');
$article->setImage($image);
$manager->persist($article);
}
$manager->flush();
}
public function getOrder(){
return 1;
}
}
in my ns\NikahBundle\DataFixtures\ORM\LoadImageData.php:
<?php
namespace ns\NikahBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use ns\NikahBundle\Entity\Image;
class LoadImageData extends AbstractFixture implements OrderedFixtureInterface
{
const MAX_NB_IMAGES = 5;
public function load(ObjectManager $manager)
{
$faker = \Faker\Factory::create();
for ($i=0; $i<self::MAX_NB_IMAGES; ++$i){
$image = new Image();
$image->setUrl($faker->imageUrl($width = 640, $height = 480));
$image->setAlt($faker->text);
$manager->persist($image);
$this->addReference('image', $image);
}
$manager->flush();
}
public function getOrder(){
return 2;
}
In my opinion, LoadImageData should be launch before LoadArticleData.
class LoadImageData extends AbstractFixture implements OrderedFixtureInterface
{
/ *** /
public function getOrder(){
return 1;
}
}
and
class LoadArticleData extends AbstractFixture implements OrderedFixtureInterface
{
/ *** /
public function getOrder(){
return 2;
}
}

How to load Symfony2 fixtures from migration class?

We've built up a set of data fixtures to seed the database with all our reference values. We are also using the DoctrineMigrationsBundle to manage schema updates. We would like to trigger the fixture load within our initial schema migration class so the system gets populated before running any additional schema updates.
I found in the docs that you can make migration classes container aware, but I can't figure out how to jump from that to calling/running the data fixtures. I haven't found any good answers on Stackoverflow or via google. Has anyone done this and can point me in the right direction? (or have suggestions on a better way to manage seed data in conjunction with schema migrations). Thanks.
This is using Symfony Version: 2.4
This is interesting question. I've found the "dirty" solution, but it works well.
namespace Application\Migrations;
use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
class Version20140811164659 extends AbstractMigration implements ContainerAwareInterface
{
private $container;
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
public function up(Schema $schema)
{
// ... your code here
}
public function postUp(Schema $schema)
{
// here you have to define fixtures dir
$this->loadFixtures('src/Acme/BlogBundle/DataFixtures/ORM');
}
public function down(Schema $schema)
{
// ... your code here
}
public function loadFixtures($dir, $append = true)
{
$kernel = $this->container->get('kernel');
$application = new \Symfony\Bundle\FrameworkBundle\Console\Application($kernel);
$application->setAutoExit(false);
//Loading Fixtures
$options = array('command' => 'doctrine:fixtures:load', "--fixtures" => $dir, "--append" => (boolean) $append);
$application->run(new \Symfony\Component\Console\Input\ArrayInput($options));
}
}
This solution simply running console command php app/console doctrine:fixtures:load --fixtures=src/Acme/BlogBundle/DataFixtures/ORM --append after "up" migration.
Sorry for poore English. If you'll find clear solution, share it ;)
I've made a migration class to address this very problem. The code is essentially inspired from the doctrine:fixtures:load command.
<?php
namespace AppBundle\Migrations;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\DBAL\Migrations\AbstractMigration;
use Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
abstract class AbstractFixturesAwareMigration extends AbstractMigration implements ContainerAwareInterface
{
private $container;
private $fixtures;
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
protected function getContainer()
{
return $this->container;
}
protected function addFixture(FixtureInterface $fixture)
{
if(null === $this->fixtures) {
$this->fixtures = new ContainerAwareLoader($this->getContainer());
}
$this->fixtures->addFixture($fixture);
return $this;
}
protected function executeFixtures($em = null, $append = true, $purgeMode = ORMPurger::PURGE_MODE_DELETE)
{
$em = $this->getContainer()->get('doctrine')->getManager($em);
$purger = new ORMPurger($em);
$purger->setPurgeMode($purgeMode);
$executor = new ORMExecutor($em, $purger);
$executor->execute($this->fixtures->getFixtures(), $append);
$this->fixtures = null;
return $this;
}
}
Usage is pretty straightforward:
<?php
namespace Application\Migrations;
use AppBundle\Migrations\AbstractFixturesAwareMigration
use Doctrine\DBAL\Schema\Schema;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Auto-generated Migration: Please modify to your needs!
*/
class Version20170726102103 extends AbstractFixturesAwareMigration
{
/**
* #param Schema $schema
*/
public function up(Schema $schema)
{
// this up() migration is auto-generated, please modify it to your needs
// [...]
}
public function postUp(Schema $schema)
{
// LoadMyData can be any fixture class
$this->addFixture(new LoadMyData());
$this->executeFixtures();
}
/**
* #param Schema $schema
*/
public function down(Schema $schema)
{
// this down() migration is auto-generated, please modify it to your needs
// [...]
}
}

Resources