Nous allons dans ce second TP réaliser le premier TP mais en repartant de zero. Il sera donc question de créer un blog avec un backoffice. Le front devra être catégorisé, paginé et lié à un utilisateur. Le backoffice sera sécurisé via un formulaire de connexion lié à une table User.
- Créer projet
- Créer controller front et back
- Créer entitées
- Post (titre, description, date_add, date_update, views)
- Category (titre)
- User (ne rajouter aucun champs)
- Créer CRUD
- Pagination
- Menu par catégorie
- UserInterface
- Login front
- Créer SecurityController
- Sécuriser CRUD
- Intégration template
- Bootstrap par exemple
- Template homepage (Grosse une et 4 articles en dessous sur une seule colonne)
- Template rubrique et article (Colonne contenu + sidebar)
- Formulaire de recherche
- Affichage des Articles les plus lus dans sidebar
- Incrémentation de vues
- Affichage dans sidebar des 5 articles les plus vues
- Afficher des statistiques graphiques dans le back
- On rajoute un upload d'image pour Post
- Commentaires
- Créer entitée Commentaire (texte, enable, date_add)
- Liéer à un article et un utilisateur
- Formulaire dans l'article
- Modération
- Les catégories peuvent être hiérarchisées, catégorie parent et catégories enfant
- Utiliser les validations de formulaire (via des @Assert) pour afficher des messages d'erreurs.
- Multilangue
On utilisera au maximum les annotations.
- Créer un projet symfony avec symfony
symfony new nom_projet
- Créer une entité
php app/console doctrine:generate:entity
- Générer les Getter/Setter des entités
php app/console doctrine:generate:entities
- Mis à jour de la base de données
php app/console doctrine:schema:update --force
- Génération CRUD
php app/console generate:doctrine:crud --entity=MonBundle:monEntity
-
Pour mettre à jour une entité, on modifie la classe, on met à jour les getters / setter et on update la base de données.
-
Utiliser le theme bootstrap pour les formulaires : #app/config/config.yml
twig: form: resources: ['bootstrap_3_layout.html.twig']
- Fonctions Twig pour récupérer le path des fichiers à partir du répertoire web
{{ asset('monRepertoire/monFichier.css') }}
-
Concaténation Twig : "maString" ~ "monAutreString"
-
Redirection dans un controller
$this->redirect($this->generateUrl("homepage"));
- Fabrication liens twig :
{{ path('library_search', {'page': 1 , 'form':criteres.form}) }}
ou
{{ url('library_search', {'page': 1 , 'form':criteres.form}) }}
- Méthodes de recherches d'entités
$monEntite->find( xx ); // où xx est un id ; on choisit qu'un seul élément
$monEntite->findBy( array('critere_where' => valeurRecherchee), array('critere_tri' => 'desc' ), $limit, $offset ) ; // on choisit plusieurs éléments validant le tableau de recherche
$monEntite->findOneBy( array() ); // on choisit un seul élément validant le tableau de recherche
- Query Builder
public function findByAuthorAndDate($author, $year)
{
$qb = $this->createQueryBuilder('a');
$qb->where('a.author = :author')
->setParameter('author', $author)
->andWhere('a.date < :year')
->setParameter('year', $year)
->orderBy('a.date', 'DESC')
;
return $qb
->getQuery()
->getResult()
;
}
ou
$query = $this->_em->createQuery('SELECT a FROM Advert a WHERE a.id = :id');
$query->setParameter('id', $id);
return $query->getSingleResult();
- Pagination :
use Doctrine\ORM\Tools\Pagination\Paginator;
$dql = "SELECT p, c FROM BlogPost p";
$query = $entityManager->createQuery($dql)
->setFirstResult(0)
->setMaxResults(100);
$paginator = new Paginator($query);
$c = count($paginator);
foreach ($paginator as $post) {
echo $post->getHeadline() . "\n";
}
ou
// on compte le nombre d'article maximum via une query builder dans le repository (function countArticle()) // que l'on va diviser par le nombre d'élément souhaité par page pour avoir le nombre de page pour la pagination $query = $this->createQueryBuilder('a') ->select('COUNT(a)')
->getQuery(); return $query->getSingleScalarResult();
- Relations entités
/** * @ORM\OneToMany(targetEntity="DeskComment") */ protected $comments;
/** * @ORM\ManyToOne(targetEntity="Desk") */ protected $desk;
- Formulaire simple sans classe Type
// src/Acme/TaskBundle/Controller/DefaultController.php
namespace Acme\TaskBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Acme\TaskBundle\Entity\Task;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
public function newAction(Request $request)
{
$task = $this->createForm(new Task());
$task->setTask('Mon titre');
$task->seteDate(new \DateTime());
$form = $this->createFormBuilder($task)
->add('task', 'text')
->add('date', 'date')
->getForm();
return $this->render('AcmeTaskBundle:Default:new.html.twig', array(
'form' => $form->createView()
));
}
}
- Pour styliser un formulaire vous pouvez utiliser la méthode twig form_theme
Sécurité :
- loginAction en ayant les inputs name à username et password
public function loginAction()
{
$helper = $this->get('security.authentication_utils');
return $this->render('AcmeSecurityBundle:Security:login.html.twig', array(
'last_username' => $helper->getLastUsername(),
'error' => $helper->getLastAuthenticationError(),
));
}
- SecurityController
namespace ...
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\SecurityContext;
class SecurityController extends Controller
{ ... }
- Routing.yml
login: pattern: /login
defaults: { _controller: AcmeSecurityBundle:Security:login }
login_check:
pattern: /login_check
- Security.yml
security:
firewalls:
secured_area:
pattern: ^/
anonymous: ~
form_login: ~
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
UserInterface : http://symfony.com/doc/current/cookbook/security/custom_provider.html
UserLogin :
- http://symfony.com/doc/current/cookbook/security/entity_provider.html#security-crete-user-entity
- http://symfony.com/doc/current/cookbook/security/form_login_setup.html
- http://symfony.com/doc/current/cookbook/security/form_login.html (optionnel)
- Installer Symfony http://symfony.com/download
- En ligne de commande se place dans le répertoire web racine (htdocs, www, ...)
- Exécuter la commande php symfony new nom_du_repertoire_du_projet
- Vous aurez alors un répertoire nom_du_repertoire_du_projet/ avec tous les éléments pour commencer un site sous symfony
- J'utiliserais dans la suite du corrigé le répertoire courant : nom_du_repertoire_du_projet/.
- Nous aurons besoin au minimum d'un controller front et back (que nous remplaceront après) , ces controllers vont nous permettre d'approcher un site avec un backoffice BackController permettant l'administration du site
- Il suffit de copier DefaultController dans le répertoire (nom_du_repertoire_du_projet/)src/Controller et de le nommer BackController et un autre fichier FrontController
- Pensez à modifier le nom des classes dans les fichiers et les noms des routes sinon ils entreront en conflit, des routes ne pouvant avoir 2 noms identiques
- Créer entitées, pour créer des entités il faudra se positioner en ligne de commande sur le répertoire courant (nom_du_repertoire_du_projet/)
- Exécuter la commande : php app/console doctrine:generate:entity
- -A "The entitty shortcut name : " Renseigner le nom de l'entité précédé du nom du bundle suivi de 2 points : exemple AppBundle:User, ce sera la façon de nommer les entités, une entité pouvant être créé dans plusieurs bundles, ils peuvent avoir donc le même nom d'un bundle à l'autre.
- -Laissez en annotation
- -A "New field name" Saisir le nom de la propriété souhaitée par exemple title
- -A "Field type" Mettez le type souhaité dans les types possibles : string, array, float, text, datetime ...
- -Selon le type on vous demandera la largeur du champs de caratcère (string par exemple)
- -Ensuite on repart sur "New field name" ... Continuez à saisir les propriétés de vos entités (les noms des champs de la table correspondante dans la base de données)
- -Une fois que vous avez tout saisi, vous retombez sur "New Field name" pour en sortir il suffit de taper [entrée]
- -On vous demande si vous voulez générer le repository, mettez Yes
- -Voulez vous confirmer ? Yes
- Voila l'entité est créé et présente dans src/AppBundle/Entity
- Une entité va être votre classe PHP liée à une table dans la base de données, seulement ici nous avons seulement créé le fichier php et nom la table correspondance,
- Pour lier cette entité à une table dans la base de données il suffit de saisir cette commande :
- php app/console doctrine:schema:update --force
- Ensuite si tout se passe bien vous aurez dans votre phpmyadmin la table correspondance avec les champs souhaités.
- S'il y a une erreur de connexion il faudra aller dans app/config/parameters.yml qui est un fichier contenant les accès à votre base de données, il est évident que c'est un fichier sensible, ne pas l'inclure dans vos exports
- Les fichiers yml utilisent les espaces comme moyen de structurer les données, un peu comme du JSON avec les {} et les xml avec les balises, il faudra espacer les éléments enfants pour qu'ils soient inclus dedans, ne pas utiliser de tabulation, seulement des espaces, je vous invite à vous documenter sur le YAML
- Créer les 3 entités avec les champs souhaités (Post, Category, User)
- Pour créer les CRUD (backoffice de gestion) vous avez 2 solutions, soit le faire manuellement en créant les fonctions dans BackController et de créer toutes les vues (liste, ajout, suppression, modification), soit d'utiliser une ligne de commande qui va tout vous générer :
- php app/console doctrine:generate:crud
- -A "the Entity shortcut name" Saisir le nommage de votre entité : NomBundle:NomEntité (AppBundle:Post)
- -A "Do you want to generate the write actions" Yes (ceci va générer les actions et les vues concernant les méthodes d'ajout et de modification
- -Laissez en annotation
- -A"Route prefix" : saisir ce que vous voulez ce sera l'url d'appel de votre CRUD pour l'entité souhaité (par exemple /admin/post)
- -Confirmez
- Ensuite vos controller, vues et formulaire sont générés dans votre bundle :
- src/AppBundle/Controller, src/AppBundle/Entity, src/AppBundle/Form, src/AppBundle/Resources/views
- Via le CRUD générez plusieurs entrées dans Post, on pourra ainsi travailler sur la pagination.
- Dans DefaultController ou FrontController selon votre projet, modifiez IndexAction, vous pouvez récupérer l'IndexAction du premier TP pour réaliser la pagination
- Idem pour menu par catégorie