[RESOLU] [Symfony 2.2] Utilisation de DoctrineFixturesBundle

Eléphant du PHP | 398 Messages

12 mai 2014, 18:25

Bonjour,

Dans le cadre d'un test technique, je dois développer un petit site d'e-commerce le plus simple possible avec Symfony. Du coup, je fais mes grands débuts avec ce framework après avor passé pas mal de temps sous Zend :D mais, dans ma qualité de n00b, je suis confronté à un soucis concernant l'insertion de données via Doctrine 2 (autant s'amuser avec ça, je n'avait pas utiliser encore ^^).
J'ai deux tables, créés avec Doctrine : Products et CategoriesProducts. J'ai créé les Entities via Doctrine et elles donnent ça :
namespace Bdc\BoutiqueBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * CategoriesProducts
 *
 * @ORM\Table()
 * @ORM\Entity(repositoryClass="Bdc\BoutiqueBundle\Entity\CategoriesProductsRepository")
 */
class CategoriesProducts
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="categorie_name", type="string", length=255)
     */
    private $categorieName;


    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set categorieName
     *
     * @param string $categorieName
     * @return CategoriesProducts
     */
    public function setCategorieName($categorieName)
    {
        $this->categorieName = $categorieName;
    
        return $this;
    }

    /**
     * Get categorieName
     *
     * @return string 
     */
    public function getCategorieName()
    {
        return $this->categorieName;
    }
}
et
namespace Bdc\BoutiqueBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Products
 *
 * @ORM\Table()
 * @ORM\Entity(repositoryClass="Bdc\BoutiqueBundle\Entity\ProductsRepository")
 */
class Products
{
    
    /**
     * @ORM\ManyToOne(targetEntity="Bdc\BoutiqueBundle\Entity\CategoriesProducts")
     * @ORM\JoinColumn(nullable=false)
     */
    private $categorieProduct;
    
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name_product", type="string", length=255)
     */
    private $nameProduct;

	/**
     * @var string
     *
     * @ORM\Column(name="description_product", type="string", length=255)
     */
	private $description;
	
	/**
     * @var string
     *
     * @ORM\Column(name="image_product", type="string", length=255)
     */
	private $image;
	
	
    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set nameProduct
     *
     * @param string $nameProduct
     * @return Products
     */
    public function setNameProduct($nameProduct)
    {
        $this->nameProduct = $nameProduct;
    
        return $this;
    }

    /**
     * Get nameProduct
     *
     * @return string 
     */
    public function getNameProduct()
    {
        return $this->nameProduct;
    }

    /**
     * Set description
     *
     * @param string $description
     * @return Products
     */
    public function setDescription($description)
    {
        $this->description = $description;
    
        return $this;
    }

    /**
     * Get description
     *
     * @return string 
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Set categorieProduct
     *
     * @param \Bdc\BoutiqueBundle\Entity\CategoriesProducts $categorieProduct
     * @return Products
     */
    public function setCategorieProduct(\Bdc\BoutiqueBundle\Entity\CategoriesProducts $categorieProduct)
    {
        $this->categorieProduct = $categorieProduct;
    
        return $this;
    }

    /**
     * Get categorieProduct
     *
     * @return \Bdc\BoutiqueBundle\Entity\CategoriesProducts 
     */
    public function getCategorieProduct()
    {
        return $this->categorieProduct;
    }

    /**
     * Set image
     *
     * @param string $image
     * @return Products
     */
    public function setImage($image)
    {
        $this->image = $image;
    
        return $this;
    }

    /**
     * Get image
     *
     * @return string 
     */
    public function getImage()
    {
        return $this->image;
    }
}
Pour créé la relation de clé étrangère entre les deux tables j'ai donc ajouté ceci :
    /**
     * @ORM\ManyToOne(targetEntity="Bdc\BoutiqueBundle\Entity\CategoriesProducts")
     * @ORM\JoinColumn(nullable=false)
     */
    private $categorieProduct;
La création des deux tables est nickel, avec la Fk et tout ce qui va avec !!
Maintenant, pour insérer des données automatiquement, j ai vu que je pouvais utiliser DoctrineFixturesBundle.
Pour remplir ma table CategoriesProducts, j'ai créé cette classe :
/**
 * Insertion de données dans la table CategoriesProducts
 */
namespace Bdc\BoutiqueBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Bdc\BoutiqueBundle\Entity\CategoriesProducts;

class LoadCategoriesProductsData implements FixtureInterface
{
	public function load (ObjectManager $manager)
	{
		$datas = array("Film", "Musique", "Livre");
		
		foreach($datas as $i => $nom)
	    {
	      // On crée la compétence
	      $liste_competences[$i] = new CategoriesProducts();
	      $liste_competences[$i]->setCategorieName($nom);
	      // On la persiste
	      $manager->persist($liste_competences[$i]);
	    }  
	                              
	    // On déclenche l'enregistrement
	    $manager->flush();
	}
}
et je bloque sur l'insertion dans la table Products avec un id de catégorie. J'ai codé comme ça :
namespace Bdc\BoutiqueBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Bdc\BoutiqueBundle\Entity\Products;
use Bdc\BoutiqueBundle\Entity\CategoriesProducts;

class LoadProductsData implements FixtureInterface
{
	public function load (ObjectManager $manager)
	{
		$datas = array(
array(
				"name_product" => "Le Seigneur des Anneaux", 
				"categorieProduct_id" => "Film", 
				"description_product" => "", 
				"image_product" => "http://www.collecttolkien.com/images/Media/Movies%20LOTR%20Trilogy%20DVD%20Box%20Set%20May252004%20$69.jpg"
			),
			array(
				"name_product" => "Star Wars", 
				"categorieProduct_id" => "Film", 
				"description_product" => "", 
				"image_product" => "http://pmcdn.priceminister.com/photo/893150047.jpg"
			),
// etc etc... avec plein d'autres données
		);
		
		foreach($datas as $i => $tabProducts) {
			// On crée la compétence
	      	$liste_competences[$i] = new Products();
	      	$liste_competences[$i]->setNameProduct($tabProducts["name_product"]);
	      	$liste_competences[$i]->setDescription($tabProducts["description_product"]);
	      	$liste_competences[$i]->setImage($tabProducts["image_product"]);
	      	 // Gestion CategoriesProducts
	      	$oCategorieProduct = new CategoriesProducts();
	      	$oCategorieProduct->setCategorieName($tabProducts["categorieProduct_id"]);
	      	$liste_competences[$i]->setCategorieProduct($oCategorieProduct);
	      	
	      	// On la persiste
	      	$manager->persist($liste_competences[$i]);
	    }  
	                              
	    // On déclenche l'enregistrement
	    $manager->flush();
	}
}
Pour lancer l isnertion en CLI :

Code : Tout sélectionner

stephane@balistik-pc:~/workspace/bedycasa$ php app/console doctrine:fixtures:load Careful, database will be purged. Do you want to continue Y/N ?y > purging database > loading Bdc\BoutiqueBundle\DataFixtures\ORM\LoadCategoriesProductsData > loading Bdc\BoutiqueBundle\DataFixtures\ORM\LoadProductsData [Doctrine\ORM\ORMInvalidArgumentException] A new entity was found through the relationship 'Bdc\BoutiqueBundle\Entity\Products#categorieProduct' that was not configured to cascade persist operations for entity: Bdc\BoutiqueBundle\Entity\Categories Products@00000000332c9adf000000006224c514. To solve this issue: Either explicitly call EntityManager#p ersist() on this unknown entity or configure cascade persist this association in the mapping for exam ple @ManyToOne(..,cascade={"persist"}). If you cannot find out which entity causes the problem impleme nt 'Bdc\BoutiqueBundle\Entity\CategoriesProducts#__toString()' to get a clue. doctrine:fixtures:load [--fixtures[="..."]] [--append] [--em="..."] [--purge-with-truncate]
Au début, j'utilisai les numéros d'Id mais j'avais également un soucis, ou il me demandais d'instancier la classe CategoriesProducts (normal vu le code de l'Entitie).
Merci d'avance ^^
----------------------------------------------------------------------------------
https://astro-otter.space - Discover wonders and mysteries of Universe

Eléphant du PHP | 127 Messages

12 mai 2014, 22:44

Bonsoir,

au début avec Doctrine, l'erreur "A new entity was found through the relationship" est normale (et classique).
Tu essaies de persister une entité (product) contenant une relation avec une autre entité qui n'est pas persistée ou issue de l'EntityManager (categorieProduct).

Et effectivement, categorieProduct vient d'être créée et tu ne demandes pas explicitement d'enregistrement les données liéés.

Pour résoudre 2 solutions s'offrent à toi :
- récupérer les catégorieProduct issus de l'EntityManager pour les associer à tes product (tu peux utiliser Doctrine\Common\DataFixtures\OrderedFixtureInterface pour garantir que tes catégories sont créées AVANT tes produits)
- indiquer à Doctrine de persister les catégorieProducts en cascade (une annotation cascade={"persist"} sur ta relation ManyToOne Products::$categorieProduct devrait faire l'affaire)

Bon courage.
A+

Avatar du membre
Administrateur PHPfrance
Administrateur PHPfrance | 13231 Messages

12 mai 2014, 22:46

Regarde cette doc : http://symfony.com/doc/current/bundles/ ... index.html
Surtout le passage "Sharing Objects between Fixtures"
Connaître son ignorance est la meilleure part de la connaissance
Pour un code lisible : n'hésitez pas à sauter des lignes et indenter

twitter - site perso - Github - Zend Certified Engineer

Eléphant du PHP | 398 Messages

13 mai 2014, 11:31

Regarde cette doc : http://symfony.com/doc/current/bundles/ ... index.html
Surtout le passage "Sharing Objects between Fixtures"
C'est exactement ce tuto que j'ai suivi (en plus de celui d'OpenClassRoom ^^), je regarde cette partie.

nhachet : je vais essayer avec le cascade dans un premier temps :)


EDIT : la deuxieme solution de nhachet rejoint le post de zeus en fait, la solution semble se diriger par là on dirait ^^
----------------------------------------------------------------------------------
https://astro-otter.space - Discover wonders and mysteries of Universe

Eléphant du PHP | 398 Messages

13 mai 2014, 13:54

Voila c'est résolu :). Code des Fixtures :

Pour les catégories :
namespace Bdc\BoutiqueBundle\DataFixtures\ORM;

//use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Bdc\BoutiqueBundle\Entity\CategoriesProducts;

class LoadCategoriesProductsData extends AbstractFixture implements OrderedFixtureInterface //implements FixtureInterface
{
	public function load (ObjectManager $manager)
	{
		$datas = array("Film", "Musique", "Livre");
		
		foreach($datas as $i => $nom)
	    {
	      // On crée la compétence
	      $liste_competences[$i] = new CategoriesProducts();
	      $liste_competences[$i]->setCategorieName($nom);
	      // On la persiste
	      $manager->persist($liste_competences[$i]);
	      
	      $this->addReference($nom, $liste_competences[$i]);
	    }  
	                              
	    // On déclenche l'enregistrement
	    $manager->flush();
	}
	
	/**
     * {@inheritDoc}
     */
    public function getOrder()
    {
        return 1; // the order in which fixtures will be loaded
    }
}
Et pour les produits :
namespace Bdc\BoutiqueBundle\DataFixtures\ORM;

//use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Bdc\BoutiqueBundle\Entity\Products;
use Bdc\BoutiqueBundle\Entity\CategoriesProducts;

class LoadProductsData extends AbstractFixture implements OrderedFixtureInterface //implements FixtureInterface
{
	public function load (ObjectManager $manager)
	{
		$datas = array(
			array(
				"name_product" => "Le Seigneur des Anneaux", 
				"categorieProduct_id" => "Film", 
				"description_product" => "", 
				"image_product" => "http://www.collecttolkien.com/images/Media/Movies%20LOTR%20Trilogy%20DVD%20Box%20Set%20May252004%20$69.jpg"
			),
			array(
				"name_product" => "Star Wars", 
				"categorieProduct_id" => "Film", 
				"description_product" => "", 
				"image_product" => "http://pmcdn.priceminister.com/photo/893150047.jpg"
			),
			array(
				"name_product" => "Metallica - Orion", 
				"categorieProduct_id" => "Musique", 
				"description_product" => "", 
				"image_product" => "http://thedamnriothouse.files.wordpress.com/2012/06/metallicaorionmusicmore.jpg"
			),
			array(
				"name_product" => "Bob Marley - Live", 
				"categorieProduct_id" => "Musique", 
				"description_product" => "", 
				"image_product" => "http://4.bp.blogspot.com/-BGxPL1uZde8/Tzejq6NRU_I/AAAAAAAABG8/WIa3lNmce08/s1600/bmw-live75.jpg"
			),
			array(
				"name_product" => "PHP 5", 
				"categorieProduct_id" => "Livre", 
				"description_product" => "", 
				"image_product" => "http://www.aurelielucet.com/images/couvs/3.png"
			),
			array(
				"name_product" => "One Piece Tome 65", 
				"categorieProduct_id" => "Livre", 
				"description_product" => "", 
				"image_product" => "http://www.images-booknode.com/book_cover/801/full/one-piece,-tome-65---table-rase-801152.jpg"
			),
			array(
				"name_product" => "Le sang du dragon - tome 1", 
				"categorieProduct_id" => "Livre", 
				"description_product" => "", 
				"image_product" => "http://img.bd-sanctuary.com/bds/big/le-sang-du-dragon-bd-volume-1-simple-9108.gif"
			),
		);
		
		foreach($datas as $i => $tabProducts) {
			// On crée la compétence
	      	$liste_competences[$i] = new Products();
	      	$liste_competences[$i]->setNameProduct($tabProducts["name_product"]);
	      	$liste_competences[$i]->setDescription($tabProducts["description_product"]);
	      	$liste_competences[$i]->setImage($tabProducts["image_product"]);
			
			// Gestion des categories
	      	$liste_competences[$i]->setCategorieProduct($this->getReference($tabProducts["categorieProduct_id"]));
	      	
	      	// On la persiste
	      	$manager->persist($liste_competences[$i]);
	    }  
	                              
	    // On déclenche l'enregistrement
	    $manager->flush();
	}
	
	/**
     * {@inheritDoc}
     */
    public function getOrder()
    {
        return 2; // the order in which fixtures will be loaded
    }
}
Merci à vous deux :)
----------------------------------------------------------------------------------
https://astro-otter.space - Discover wonders and mysteries of Universe

Avatar du membre
Administrateur PHPfrance
Administrateur PHPfrance | 13231 Messages

13 mai 2014, 23:07

Je viens de me rappeler que pour un projet avec des dépendances un peu chelou, j'avais utilisé ça : https://github.com/doctrine/data-fixtur ... einterface

L'intérêt est que si tu déplaces ton order 1, tu n'as pas à modifier tout ceux qui en dépendent.

PS : si ton problème est résolu, penses à le signaler comme résolu
Connaître son ignorance est la meilleure part de la connaissance
Pour un code lisible : n'hésitez pas à sauter des lignes et indenter

twitter - site perso - Github - Zend Certified Engineer