installation, paquets utiles : php composer.phar req server vardumper annotations doctrine twig asset
php composer.phar req asset
Préparation (paquet à installer) : cette commande nécessite le Bundle maker-bundle php composer.phar req maker-bundle
. Pour connaître ce que cette commande permet de générer php bin/console list make
documentation documentation 2
* Créer un fichier src/Controller/ProduitController.php
php bin/console make:controller ProduitController
Dans le contrôleur :
/**
* @Route("/produit", name="produit.index")
* @Route("/", name="produit.index2")
*/
public function index()
{
return $this->redirectToRoute('produit.show');
}
exemple de code source : méthode showProduits
use Twig\Environment; // template TWIG
use Symfony\Bridge\Doctrine\RegistryInterface; // ORM Doctrine
use Symfony\Component\HttpFoundation\Request; // objet REQUEST
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
// dans les annotations @Method
{% extends "base.html.twig" %}
{% block body %}
<div class="container">
<div class="row">
<a href="{#{ path('produit.add') }#}"> Ajouter un produit </a>
<table>
<caption>Recapitulatifs des produits</caption>
<thead>
<tr><th>nom</th><th>id</th><th>type</th><th>prix</th><th>nom photo</th><th>photo</th>
<th>opération</th>
</tr>
</thead>
<tbody>
{% for produit in data if data is not empty %}
<tr>
<td>{{produit.nom}}</td><td>{{produit.id}}</td><td>{{produit.typeProduit_id}}</td><td>{{produit.prix}}</td><td>
{{produit.photo}}</td><td>
<img style="width:40px;height:40px" src="{{asset('images/')}}{{produit.photo}}" alt="image du produit" >
</td>
<td>
<a href="{#{ path('produit.edit', {id: produit.id}) }#}">modifier</a>
<a href="{#{ path('produit.delete', {id: produit.id}) }#}">supprimer</a>
</td>
</tr>
{% else %}
<tr><td>Pas de Produits</td></tr>
{% endfor %}
<tbody>
</table>
</div>
</div>
{% endblock %}
$produits = [
['id' => 1,'nom' => 'Enveloppes (50p)', 'prix' => '2', 'typeProduit_id' => '1', 'photo' => ''],
['id' => 2,'nom' => 'Stylo noir', 'prix' => '1', 'typeProduit_id' => '1', 'photo' => ''],
['id' => 3,'nom' => 'Boite de rangement', 'prix' => '3', 'typeProduit_id' => '1', 'photo' => ''],
['id' => 4,'nom' => 'Chaise', 'prix' => '40', 'typeProduit_id' => '2', 'photo' => ''],
['id' => 5,'nom' => 'Tables, 'prix' => '200', 'typeProduit_id' => '2', 'photo' => '']
];
php bin/console make:entity Produit
/**
* @var string
*
* @ORM\Column(name="nom", type="string", length=255)
*/
private $nom;
/**
* @var decimal
*
* @ORM\Column(name="prix", type="decimal", precision=6, scale=2, nullable=true)
*/
private $prix;
/**
* @var string
*
* @ORM\Column(name="photo", type="string", length=255, nullable=true)
*/
private $photo;
/**
* @var int
*
* @ORM\Column(name="typeProduit_id", type="integer", nullable=true)
*/
private $typeProduit_id;
* @ORM\Table(name="produits")
bin/console doctrine:schema:update --dump-sql
bin/console doctrine:schema:update --force
ajouter les getters et les setters(cocher Fluent setters) avec phpStorm
Ajouter quelques enregistrements dans la table
INSERT INTO produits (`id`, `typeProduit_id`, `nom`, `prix`,`photo`)VALUES (NULL, 1, 'Enveloppes (50p)', '2.00',null);
INSERT INTO produits (`id`, `typeProduit_id`, `nom`, `prix`,`photo`)VALUES (NULL, 1, 'Stylo noir', '2.50',null);
INSERT INTO produits (`id`, `typeProduit_id`, `nom`, `prix`,`photo`)VALUES (NULL, 1, 'Boite de rangement', '1.50',null);
INSERT INTO produits (`id`, `typeProduit_id`, `nom`, `prix`,`photo`)VALUES (NULL, 1,'Chaise', '40',null);
INSERT INTO produits (`id`, `typeProduit_id`, `nom`, `prix`,`photo`)VALUES (NULL, 1,'Table', '200',null);
$produits=$doctrine->getRepository(Produit::class)->findAll();
dump($produits);
ne pas oublier les instructions pour utiliser le namespace de l’entité Produit : use App\Entity\Produit;
{{produit.typeProduit_id}}
pour {{produit.typeProduitId}}
ou renommer le setter getTypeProduit_id et getter setTypeProduit_id dans l’entité Produit
créer une entité TypeProduit
php bin/console make:entity TypeProduit
....
* @ORM\Table(name="typeProduits")
....
/**
* @var string
*
* @ORM\Column(name="libelle", type="string", length=255)
*/
private $libelles;
bin/console doctrine:schema:update --dump-sql
bin/console doctrine:schema:update --force
ajouter les getters et les setters(cocher Fluent setters) avec phpStorm
Ajouter quelques enregistrements dans la table
INSERT INTO typeProduits (`id`, `libelle`)VALUES (NULL,'Fourniture de bureau');
INSERT INTO typeProduits (`id`, `libelle`)VALUES (NULL,'Mobilier');
php bin/console doctrine:query:sql "UPDATE produits SET typeProduit_id = NULL"
documentation http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html
mettre un lien de type ManyToOne au lieu de la description du champ sur
/**
* @ORM\ManyToOne(targetEntity="TypeProduit")
* @ORM\JoinColumn(name="typeProduit_id", referencedColumnName="id")
*/
private $typeProduitId;
avec une mise à jour sur le getter et le setter
/**
* @return int
*/
public function getTypeProduitId()
{
return $this->typeProduitId;
}
/**
* @param int $typeProduitId
* @return Produit
*/
public function setTypeProduitId($typeProduitId)
{
$this->typeProduitId = $typeProduitId;
return $this;
}
Remarque : pour fonctionner dans les anciennes versions @JoinColumn
suffisait en annotation. Maintenant il faut soit ajouter un lien sur un namespace pour utiliser la classe qui fait référence à l’annotation, donc soit ajouter @JoinColumn : use Doctrine\ORM\Mapping\JoinColumn;
, soit rajouter @ORM
dans la vue pour afficher le libellé du type du produit
{{ produit.typeProduitId.libelle | default("id Null")}}
$produit=$doctrine->getRepository(Produit::class)->find(1);
$typeProduit=$doctrine->getRepository(TypeProduit::class)->find(1);
$produit->setTypeProduitId($typeProduit);
$doctrine->getEntityManager()->persist($produit);
$doctrine->getEntityManager()->flush(); // commit des opérations
créer une vue addProduit.html.twig
{% extends "base.html.twig" %}
{% block body %}
<form method="post" action="{{ path('produit.validFormAdd') }}">
<div class="row">
<fieldset>
<legend>Creer un produit (twig)</legend>
<label>Nom :
<input name="nom" type="text" size="18" value="{{donnees.nom|default('')}}" />
{% if erreurs.nom is defined %}
<small class="error">{{erreurs.nom}}</small>
{% endif %}
</label>
<label>Type :
<select name="typeProduit_id">
{% if donnees.typeProduit_id is not defined or donnees.typeProduit_id is empty %}
<option value="">Veuillez sélectionner un produit</option>
{% endif %}
{% for typeProduit in typeProduits %}
<option value="{{ typeProduit.id }}"
{% if donnees.typeProduit_id is defined and typeProduit.id == donnees.typeProduit_id %}selected{% endif %}>
{{ typeProduit.libelle }}
</option>
{% endfor %}
</select>
{% if erreurs.typeProduit_id is defined %}
<small class="error">{{erreurs.typeProduit_id}}</small>
{% endif %}
</label>
<label>Prix
<input name="prix" type="text" size="18" value="{{donnees.prix|default('')}}"/>
{% if erreurs.prix is defined %}
<small class="error">{{erreurs.prix}}</small>
{% endif %}
</label>
<label>Photo
<input name="photo" type="text" size="18" value="{{donnees.photo|default('')}}"/>
{% if erreurs.photo is defined %}
<small class="error">{{erreurs.photo}}</small>
{% endif %}
</label>
<input type="submit" name="creerProduit" value="créer" />
</fieldset>
</div>
</form>
{% endblock %}
dans le contrôleur, ajouter 2 méthodes :
/**
* @Route("/produit/add",name="produit.add")
* @Method({"GET"})
*/
public function addProduit(Request $request, Environment $twig, RegistryInterface $doctrine)
{
// A modifier
$conn = $this->get('database_connection');
$typeProduits = $conn->fetchAll('SELECT id,libelle
FROM typeProduits ORDER BY id;');
// fin A modifier
return $this->render('produit/addProduit.html.twig', ['typeProduits'=> $typeProduits]);
}
/**
* @Route("/produit/validFormAdd",name="produit.validFormAdd")
* @Method({"POST"})
*/
public function validFormAddAction(Request $request, Environment $twig, RegistryInterface $doctrine)
{
$donnees['nom']=htmlspecialchars($_POST['nom']);
$donnees['prix']=htmlspecialchars($_POST['prix']);
$donnees['photo']=$request->request->get('photo');
$donnees['typeProduit_id']=htmlentities($request->request->get('typeProduit_id'));
$erreurs=array();
if ((! preg_match("/^[A-Za-z ]{2,}/",$donnees['nom']))) $erreurs['nom']='nom composé de 2 lettres minimum';
if(! is_numeric($donnees['typeProduit_id']))$erreurs['typeProduit_id']='veuillez saisir une valeur';
if(! is_numeric($donnees['prix']))$erreurs['prix']='saisir une valeur numérique';
if (! preg_match("/[A-Za-z0-9]{2,}.(jpeg|jpg|png)/",$donnees['photo'])) $erreurs['photo']='nom de fichier incorrect (extension jpeg , jpg ou png)';
if(! empty($erreurs))
{
// A modifier
$conn = $this->get('database_connection');
$typeProduits = $conn->fetchAll('SELECT id,libelle FROM typeProduits ORDER BY id;');
// fin A modifier
return $this->render('produit/addProduit.html.twig', ['donnees'=>$donnees,'erreurs'=>$erreurs,'typeProduits'=> $typeProduits]);
}
else
{
// A modifier
$conn = $this->get('database_connection');
$conn->insert('produits', ["typeProduitId"=>$donnees['typeProduit_id'],"nom"=>$donnees['nom'],"prix"=>$donnees['prix'],"photo"=>$donnees['photo']]);
// fin A modifier
return $this->redirectToRoute('produit.show');
}
return $this->redirectToRoute('produit.show');
}
}
@Method
: use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
modifier la vue templates/produit/showProduits.html.twig
<td>
<a href="{#{ path('produit.edit', {id: produit.id}) }#}">modifier</a>
<a href="{#{ path('produit.delete', {id: produit.id}) }#}">supprimer</a>
<form action="{{ url('produit.delete') }}" method="POST">
<input type="hidden" name="token" value="{#{ csrf_token('delete_valid') }#}">
<input type="hidden" name="produit_id" value="{{ produit.id }}">
<input type="hidden" name="_method" value="DELETE">
<button type="submit">Supprimer</button>
</form>
</td>
</tr>
public function deleteProduit(Request $request, Environment $twig, RegistryInterface $doctrine)
{
// if(!$this->isCsrfTokenValid('command_valid', $request->get('token'))) {
// throw new InvalidCsrfTokenException('Invalid CSRF token');
// }
$id=$request->request->get('produit_id');
// A modifier
$conn = $this->get('database_connection');
$conn->delete('produits',["id"=>$id]);
// fin A modifier
return $this->redirectToRoute('produit.show');
}
/**
* @Route("/produit/edit/{id}",name="produit.edit", requirements={"id" = "\d+"})
* @Method({"GET"})
*/
public function editProduit(Request $request, Environment $twig, RegistryInterface $doctrine,$id)
{
// A modifier
$conn = $this->get('database_connection');
$statement = $conn->executeQuery('SELECT id,typeProduitId,nom,prix,photo
FROM produits WHERE id= ? LIMIT 1;',[$id]);
$produit = $statement->fetch();
$typeProduits = $conn->fetchAll('SELECT id,libelle
FROM typeProduits ORDER BY id;');
// fin A modifier
return $this->render('produit/editProduit.html.twig', ['donnees' => $produit,'typeProduits'=> $typeProduits]);
}
/**
* @Route("/produit/validFormEdit",name="produit.validFormEdit")
* @Method({"PUT"})
*/
public function validFormEditProduit(Request $request)
{
$donnees['id']=$request->request->get('id');
$donnees['nom']=htmlspecialchars($_POST['nom']);
$donnees['prix']=htmlspecialchars($_POST['prix']);
$donnees['photo']=$request->request->get('photo');
$donnees['typeProduit_id']=htmlentities($request->request->get('typeProduit_id'));
$erreurs=array();
if ((! preg_match("/^[A-Za-z ]{2,}/",$donnees['nom']))) $erreurs['nom']='nom composé de 2 lettres minimum';
if(! is_numeric($donnees['typeProduit_id']))$erreurs['typeProduit_id']='veuillez saisir une valeur';
if(! is_numeric($donnees['prix']))$erreurs['prix']='saisir une valeur numérique';
if (! preg_match("/[A-Za-z0-9]{2,}.(jpeg|jpg|png)/",$donnees['photo'])) $erreurs['photo']='nom de fichier incorrect (extension jpeg , jpg ou png)';
if(! empty($erreurs))
{
// A modifier
$conn = $this->get('database_connection');
$typeProduits = $conn->fetchAll('SELECT id,libelle FROM typeProduits ORDER BY id;');
// fin A modifier
return $this->render('produit/editProduit.html.twig', ['donnees'=>$donnees,'erreurs'=>$erreurs,'typeProduits'=> $typeProduits]);
}
else
{
// A modifier
$conn = $this->get('database_connection');
$conn->update('produits',
["typeProduitId"=>$donnees['typeProduit_id'],"nom"=>$donnees['nom'],"prix"=>$donnees['prix'],"photo"=>$donnees['photo']],
["id"=>$donnees['id']]
);
// fin A modifier
return $this->redirectToRoute('produit.show');
}
return $this->redirectToRoute('produit.show');
}
Classe dans laquelle, on crée ses propres requêtes (pour les requêtes de type select un peu compliqué) avec le language DQL (Doctrine Query Language)
Dans le fichier src/Repository/ProduitRepository.php, ajouter une méthode
public function getDetailsProduits(){
// http://symfony.com/doc/current/doctrine/associations.html#joining-related-records
// $query = $this->getEntityManager()
// ->createQuery("SELECT p.id, t.libelle, p.nom, p.prix, p.photo
// FROM App:Produit as p
// JOIN App:TypeProduit as t
// WHERE p.typeProduitId=t.id
// ORDER BY p.nom");
//
// return $query->getResult();
// $qb=$this->createQueryBuilder('p'); // il lui faut une lettre, i : item en base de données donc schéma
// $qb->select('p.id', 't.libelle', 'p.nom', 'p.prix', 'p.photo')
// ->join( 'App:TypeProduit', 't')
// ->where('p.typeProduit_id=t.id')
// ->addOrderBy('p.nom', 'ASC');
// return $qb->getQuery()->getResult();
modifier la méthode ci dessus pour calculer le nombre et le prix moyen par type de produit
Dans le contrôleur, ajouter une méthode
public function ProduitsDetails(Request $request, Environment $twig, RegistryInterface $doctrine)
{
$detailsProduits=$doctrine->getRepository(Produit::class)->getDetailsProduits();