Uno dei bisogni récorrente d’un progetto web, è di’avere bisogno di paramèmolto globali per la vostra applicazione. Généraramente, in magazzinoça in un file yml e il gioco è fattoé. Ma la situazione si complica quando il cliente chiedeàpoter mettere le mani sopra.
Lui dare gli accès FTP/SSH per modificarli? No, certamente no. Soprattutto se c’è per rifilare questo compito al tirocinante.
Bisognerà metterglieloàdisposizione di un'interfaccia di’admin con un modulo per poterli modificare.
Nel nostro caso, abbiamo bisogno solo di alcuni paramèmolto, ma questa interfaccia permetterà di gérer tanto di paramèpiù di quanto desideriamo.
Créazione della nostra tavola
Su crééun tavolo tutto bête con MySQL Workbench, o con make:entity. A scelta.
Nel nostro esempio, useremo solo 2 campi data per un déscopo e fine di’un'offerta.
Abbiamo bisogno di’un campo chiave, che conterrà la parola chiave che permetterà di puntarvi sopra. E un campo valore, che conterrà una stringaînato con il valore sérialée.
E un ultimo campo con l'ultimaère data di messaàgiorno.
Créazione della nostra interfaccia di’amministratore
Una volta che il nostro tavolo è créécon MysqlWorkbench o PhpMyAdmin, si creaée la nostra entitàé e repository
php bin/console doctrine:mapping:import "App\Entity" annotation --path=src/Entity
E affinché il nostro repository sia créé, dovremo aggiungere l’annotazione seguente e régénémescolare tutto
@ORM\Entity(repositoryClass="App\Repository\parametresRepository")
Per dare questo :
/**
* Parametres
*
* @ORM\Table(name="parametres", indexes={@ORM\Index(name="clef", columns={"clef"})})
* @ORM\Entity(repositoryClass="App\Repository\parametresRepository")
*/
class Parametres
{
Su régénèi file
php bin/console make:entity --regenerate App
Su crééil nostro admin :
php bin/console make:sonata:admin
Ed eccoàa questo stadio abbiamo crééla nostra interfaccia di’admin dei paramétrage, con un CRUD semplice.
Tranne, che non’non è ciò che’vogliamo, même stessoça funzionerei quando mêio.
Quindi andiamo créaprire una pagina di’admin où l’l'utente non potrà creareéer e modificare ciò che’lui vuole, ma solo i nostri 2 paramètre.
Par déserve, la nostra interfaccia punta sulla vista elenco. c’è questa azione che useremo.
E per sovraccaricarla dovrà créper un controller.
Poi l’aggiungere nella configurazione il nostro servizio admin aggiungendo il controller (App\Controller\ParametresController) negli argomenti :
admin.parametres:
class: App\Admin\ParametresAdmin
arguments: [~, App\Entity\Parametres, App\Controller\ParametresController]
tags:
- { name: sonata.admin, manager_type: orm, group: admin, label: Parametres }
public: true
Poi, dobbiamo cambiare l’estendere il nostro controller per che’utilizza CRUDController invece di AbstractController. Dobbiamo anche aggiungere il nostro mémetodo listAction che punta al proprio template.
<?php
namespace App\Controller;
#use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Sonata\AdminBundle\Controller\CRUDController;
class ParametresController extends CRUDController
{
public function listAction()
{
if (false === $this->admin->isGranted('LIST')) {
throw new AccessDeniedException();
}
return $this->render('Admin/parametres/listAction.html.twig', [
'controller_name' => 'ParametresController',
]);
}
}
Installare un selettore di data
Come il nostro parametroètre è una data. Dovremo installare un datepicker. La documentazione parla di eonasdan bootstrap-datetimepicker
composer require eonasdan/bootstrap-datetimepicker
php bin/console assets:install
Si aggiunge un thèmeàrametto
# config/packages/twig.yaml.yml
twig:
form_themes:
- '@SonataCore/Form/datepicker.html.twig'
E si aggiunge la référiferimento ai nostri asset in sonata_admin.yaml
assets:
extra_stylesheets:
- bundles/sonataformatter/markitup/skins/sonata/style.css
- bundles/sonataformatter/markitup/sets/markdown/style.css
- bundles/sonataformatter/markitup/sets/html/style.css
- bundles/sonataformatter/markitup/sets/textile/style.css
- css/admin.css
- build/admin.css
- css/fontawesome/css/all.css
- bundles/sonatacore/vendor/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css
extra_javascripts:
- bundles/fosckeditor/ckeditor.js
- bundles/sonataformatter/vendor/markitup-markitup/markitup/jquery.markitup.js
- bundles/sonataformatter/markitup/sets/markdown/set.js
- bundles/sonataformatter/markitup/sets/html/set.js
- bundles/sonataformatter/markitup/sets/textile/set.js
- bundles/pixsortablebehavior/js/jquery-ui.min.js
- bundles/pixsortablebehavior/js/init.js
- bundles/sonatacore/vendor/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js
- js/admin.js
Créazione del nostro modulo
In primo luogo noi andiamo créessere una mémetodo per mettereàgiorno i nostri valori secondo le nostre chiavi del nostro parametroètre. Questa méthode sarà collocatoée nel nostro repository.
<?php
namespace App\Repository;
use App\Entity\Parametres;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
class ParametresRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
$this->registry=$registry;
$this->connection=$this->registry->getManager()->getConnection();
parent::__construct($registry, Parametres::class);
}
public function updateConfig($clef,$valeur){
$em = $this->registry->getManager();
$item = $this->createQueryBuilder('p')
->andWhere('p.clef = :val')
->setParameter('val', $clef)
->getQuery()
->getOneOrNullResult();
if (!$item) {
$item = new Parametres();
$item->setValeur($valeur);
$item->setClef($clef);
$item->setUpdatedAt(date("Y-m-d H:i:s",strtotime('now')));
$em->persist($item);
}else{
$item->setValeur($valeur);
$item->setUpdatedAt(date("Y-m-d H:i:s",strtotime('now')));
}
$em->flush();
}
}
Poi, vogliamo memorizzare dei valori. Possonoêtre di tutte le nature. Quindi andremo a conservare dei élémenti sérialés.
Dobbiamo quindi modificare il nostro getter e il nostro setter della nostra entity Parametres. E mettere il nostro campo data in tipo stringa per maggiore facilitàés.
public function getValeur(): ?string
{
return unserialize($this->valeur);
}
public function setValeur(?string $valeur): self
{
$this->valeur = serialize($valeur);
return $this;
}
public function getUpdatedAt(): ?string
{
return $this->updatedAt;
}
public function setUpdatedAt(?string $updatedAt): self
{
$this->updatedAt = $updatedAt;
return $this;
}
Si può quindi créper il nostro modulo, con la sua véverifica nel nostro controller. Aggiungiamo tutto di mêmi alcune funzioni per gévedere le nostre date.
<?php
namespace App\Controller;
use Symfony\Component\Routing\Annotation\Route;
use Sonata\AdminBundle\Controller\CRUDController;
use App\Entity\Parametres;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\HttpFoundation\Request;
use Sonata\CoreBundle\Form\Type\DatePickerType;
use Sonata\CoreBundle\Form\Type\DateTimePickerType;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
class ParametresController extends CRUDController
{
private function getConfigData() {
$em = $this->container->get('doctrine.orm.entity_manager');
$config = $em->getRepository(Parametres::class)->findAll();
$data=[];
foreach($config as $c){
$data[$c->getClef()]=$c->getValeur();
}
return $data;
}
public function listAction()
{
if (false === $this->admin->isGranted('LIST')) {
throw new AccessDeniedException();
}
$request = $this->getRequest();
$locale = $this->getRequest()->getLocale();
$data = $this->getConfigData();
$formBuilder = $this->createFormBuilder(null, [
'constraints' => [new Callback([$this, 'formValidate'])]
]);
$formBuilder->add("offre_date_debut", DatePickerType::class, [
'required' => true,
'dp_use_current' => false,
'dp_min_date' => new \DateTime("2020-01-01"),
'data' => isset($data['offre_date_debut']) ? new \DateTime('@'.$this->parseDate($data['offre_date_debut'],$locale)) : new \DateTime(),
'mapped' => false,
])
->add("offre_date_fin", DatePickerType::class, [
'required' => true,
'dp_use_current' => false,
'dp_min_date' => new \DateTime("2020-01-01"),
'data' => isset($data['offre_date_fin']) ? new \DateTime('@'.$this->parseDate($data['offre_date_fin'],$locale)) : new \DateTime(),
'mapped' => false,
])
->add('submit', SubmitType::class, [
'label' => $this->get('translator')->trans('Valider')
]);
$form = $formBuilder->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$formData = $this->getRequest()->request->get('form');
$ParametresRepositoty = $this->container->get('doctrine.orm.entity_manager')->getRepository(Parametres::class);
$ParametresRepositoty->updateConfig('offre_date_debut', date("Y-m-d", $this->parseDate($formData['offre_date_debut'],$locale)) );
$ParametresRepositoty->updateConfig('offre_date_fin',date("Y-m-d",$this->parseDate($formData['offre_date_fin'],$locale)));
$this->addFlash('success', $this->get('translator')->trans('Parametres sauvegardés.'));
}
return $this->render('Admin/parametres/listAction.html.twig', [
'controller_name' => 'ParametresController',
'form' => $form->createView()
]);
}
public function formValidate($data, ExecutionContextInterface $context) {
$data = $this->getRequest()->request->get('form');
$locale = $this->getRequest()->getLocale();
if (isset($data['offre_date_debut'])) {
$offre_date_debut = $this->parseDate($data['offre_date_debut'], $locale);
$offre_date_debut = new \DateTime("@$offre_date_debut");
}
if (isset($data['offre_date_fin'])) {
$offre_date_fin = $this->parseDate($data['offre_date_fin'], $locale);
$offre_date_fin = new \DateTime("@$offre_date_fin");
}
}
public function parseDate($date, $locale, $format = 'dd LLL. y') {
$fmt = \IntlDateFormatter::create(
$locale,
\IntlDateFormatter::FULL,
\IntlDateFormatter::FULL,
'Etc/UTC',
\IntlDateFormatter::GREGORIAN,
$format
);
if (isset($date)) {
$parse_date = $fmt->parse($date);
return $parse_date;
}
return null;
}
}
La nostra ultimaèreénastro consisteàformattare la nostra pagina nel nostro template twig che abbiamo posizionatoéin templates/Admin/parametres/listAction.html.twig
{% extends '@SonataAdmin/standard_layout.html.twig' %}
{% block notice %}
{{ parent() }}
{% endblock %}
{% form_theme form _self %}
{# form_errors.html.twig #}
{% block form_errors %}
{% spaceless %}
{% if errors|length > 0 %}
{% for error in errors %}
<div class="alert alert-danger alert-dismissable">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true" aria-label="Fermer">×</button>
{{ error.message }}
</div>
{% endfor %}
{% endif %}
{% endspaceless %}
{% endblock form_errors %}
{% block sonata_admin_content %}
{% include 'SonataCoreBundle:FlashMessage:render.html.twig' %}
<div>
{{ form_errors(form) }}
<h2 class="title-border">{{ 'Paramétrage'|trans }}</h2>
<p>{{ 'Modifiaction des parametres transverse de l\'application'|trans }}</p>
<div class="sonata-ba-form">
{{ form_start(form, { attr: { class: 'form-setting-general form-theme' }}) }}
<div class="box-body container-fluid">
<div class="sonata-ba-collapsed-fields form-theme-hoz">
<div class="row">
<div class="">
<div class="box box-primary">
<div class="box-header with-border">
<h4 class="box-title">{{ 'Dates par défaut des offres'|trans }}</h4>
</div>
<div class="box-body">
<div class="sonata-ba-collapsed-fields">
<div class="form-group">
<label class="control-label required" for="form_date_manifestation">{{ 'Date de début'|trans }}</label>
<div class="sonata-ba-field sonata-ba-field-standard-natural">
{{ form_widget(form.offre_date_debut, { attr: { class: 'sonata-medium-date form-control' }}) }}
</div>
</div>
</div>
<div class="sonata-ba-collapsed-fields">
<div class="form-group">
<label class="control-label required" for="form_date_manifestation">{{ 'Date de fin'|trans }}</label>
<div class="sonata-ba-field sonata-ba-field-standard-natural">
{{ form_widget(form.offre_date_fin, { attr: { class: 'sonata-medium-date form-control' }}) }}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sonata-ba-form-actions well well-small form-actions">
{{ form_widget(form.submit, { attr: { class: 'btn btn-success' }}) }}
</div>
</div>
</div>
</div>
{{ form_end(form) }}
</div>
</div>
{% endblock %}
Ed ecco il rendering della nostra pagina
Adattare l’esempio alla versione installata
Gli esempi Symfony e Sonata rimangono legati alle versioni indicate nell’articolo. Symfony 4.x e Symfony 6.2 non sono più supportati: per un progetto attuale, utilizzate un ramo mantenuto e la documentazione corrispondente esattamente alle vostre dipendenze.
php bin/console about
composer show symfony/framework-bundle sonata-project/admin-bundle
Prima di riprendere il codice, controllate le firme, i servizi, le rotte e i template interessati. Aggiungete poi un test funzionale che copra le autorizzazioni, il codice HTTP e il risultato visibile.
Riferimento : versioni di Symfony supportate.