add settings panel

Signed-off-by: Simon Vieille <simon@deblan.fr>
This commit is contained in:
Simon Vieille 2021-05-02 20:17:39 +02:00
parent 46679ec638
commit aafa87ed41
17 changed files with 569 additions and 1 deletions

View File

@ -0,0 +1,98 @@
<?php
namespace App\Core\Controller\Setting;
use App\Core\Controller\Admin\AdminController;
use App\Core\Entity\Setting as Entity;
use App\Core\Event\Setting\SettingEvent;
use App\Core\Factory\SettingFactory as EntityFactory;
use App\Core\Manager\EntityManager;
use App\Core\Repository\SettingRepositoryQuery as RepositoryQuery;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
* @Route("/admin/setting")
*/
class SettingAdminController extends AdminController
{
/**
* @Route("/{page}", name="admin_setting_index", requirements={"page": "\d+"})
*/
public function index(
int $page = 1,
RepositoryQuery $query,
EventDispatcherInterface $eventDispatcher,
Request $request
): Response {
$eventDispatcher->dispatch(new SettingEvent(), SettingEvent::INIT_EVENT);
$pager = $query
->orderBy('.section, .label')
->paginate($page)
;
return $this->render('@Core/setting/setting_admin/index.html.twig', [
'pager' => $pager,
]);
}
/**
* @Route("/edit/{entity}", name="admin_setting_edit")
*/
public function edit(
Entity $entity,
EntityManager $entityManager,
EventDispatcherInterface $eventDispatcher,
Request $request
): Response
{
$builder = $this->createFormBuilder($entity);
$eventDispatcher->dispatch(new SettingEvent([
'builder' => $builder,
'entity' => $entity,
]), SettingEvent::FORM_INIT_EVENT);
$form = $builder->getForm();
if ($request->isMethod('POST')) {
$form->handleRequest($request);
if ($form->isValid()) {
$entityManager->update($entity);
$this->addFlash('success', 'The data has been saved.');
return $this->redirectToRoute('admin_setting_index');
}
$this->addFlash('warning', 'The form is not valid.');
}
return $this->render('@Core/setting/setting_admin/edit.html.twig', [
'form' => $form->createView(),
'entity' => $entity,
]);
}
/**
* @Route("/delete/{entity}", name="admin_setting_delete", methods={"DELETE"})
*/
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
{
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
$entityManager->delete($entity);
$this->addFlash('success', 'The data has been removed.');
}
return $this->redirectToRoute('admin_setting_index');
}
public function getSection(): string
{
return 'setting';
}
}

92
core/Entity/Setting.php Normal file
View File

@ -0,0 +1,92 @@
<?php
namespace App\Core\Entity;
use App\Core\Repository\SettingRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=SettingRepository::class)
*/
class Setting implements EntityInterface
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $section;
/**
* @ORM\Column(type="string", length=255)
*/
private $label;
/**
* @ORM\Column(type="string", length=255)
*/
private $code;
/**
* @ORM\Column(type="text", nullable=true)
*/
private $value;
public function getId(): ?int
{
return $this->id;
}
public function getSection(): ?string
{
return $this->section;
}
public function setSection(string $section): self
{
$this->section = $section;
return $this;
}
public function getLabel(): ?string
{
return $this->label;
}
public function setLabel(string $label): self
{
$this->label = $label;
return $this;
}
public function getCode(): ?string
{
return $this->code;
}
public function setCode(string $code): self
{
$this->code = $code;
return $this;
}
public function getValue()
{
return json_decode($this->value, true);
}
public function setValue($value): self
{
$this->value = json_encode($value);
return $this;
}
}

View File

@ -6,7 +6,7 @@ use App\Core\Entity\EntityInterface;
use Symfony\Contracts\EventDispatcher\Event;
/**
* class EntityEvent.
* class EntityManagerEvent.
*
* @author Simon Vieille <simon@deblan.fr>
*/

View File

@ -0,0 +1,28 @@
<?php
namespace App\Core\Event\Setting;
use Symfony\Contracts\EventDispatcher\Event;
/**
* class SettingEvent.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class SettingEvent extends Event
{
const INIT_EVENT = 'setting_event.init';
const FORM_INIT_EVENT = 'setting_event.form_init';
protected $data;
public function __construct($data = null)
{
$this->data = $data;
}
public function getData()
{
return $this->data;
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace App\Core\EventSuscriber;
use App\Core\Event\Setting\SettingEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* class SettingEventSubscriber.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class SettingEventSubscriber implements EventSubscriberInterface
{
protected static int $priority = 0;
public static function getSubscribedEvents()
{
return [
SettingEvent::INIT_EVENT => ['onInit', self::$priority],
SettingEvent::FORM_INIT_EVENT => ['onFormInit', self::$priority],
];
}
public function onInit(SettingEvent $event)
{
}
public function onFormInit(SettingEvent $event)
{
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace App\Core\Factory;
use App\Core\Entity\Setting;
/**
* class SettingFactory.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class SettingFactory
{
public function create(string $code): Setting
{
$entity = new Setting();
$entity->setCode($code);
return $entity;
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace App\Core\Repository;
use App\Core\Entity\Setting;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method Setting|null find($id, $lockMode = null, $lockVersion = null)
* @method Setting|null findOneBy(array $criteria, array $orderBy = null)
* @method Setting[] findAll()
* @method Setting[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class SettingRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Setting::class);
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace App\Core\Repository;
use Knp\Component\Pager\PaginatorInterface;
/**
* class SettingRepositoryQuery.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class SettingRepositoryQuery extends RepositoryQuery
{
public function __construct(SettingRepository $repository, PaginatorInterface $paginator)
{
parent::__construct($repository, 's', $paginator);
}
}

View File

@ -129,3 +129,6 @@
"Yes": "Oui"
"No": "Non"
"Locale": "Langue"
"Settings": "Paramètres"
"Setting": "Paramètre"
"Section": "Section"

View File

@ -67,5 +67,15 @@
</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link {{ macros_menu.active_class('setting', section) }}" href="{{ path('admin_setting_index') }}">
<span class="fa fa-sliders-h"></span>
<span class="nav-item-label">
{{ 'Settings'|trans }}
</span>
</a>
</li>
</ul>
{% endif %}

View File

@ -0,0 +1,5 @@
<div class="row">
<div class="col-md-12">
{{ form_widget(form) }}
</div>
</div>

View File

@ -0,0 +1,20 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ entity.section }}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form action="{{ path('admin_setting_edit', {entity: entity.id}) }}" id="form-entity-edit" method="POST">
{{ include('@Core/setting/setting_admin/_form.html.twig') }}
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ 'Cancel'|trans }}</button>
<button type="submit" form="form-entity-edit" class="btn btn-primary">{{ 'Save'|trans }}</button>
</div>
</div>
</div>

View File

@ -0,0 +1,21 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
{{ 'Filter'|trans }}
</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form action="{{ path('admin_site_page_index') }}" id="form-filters" method="GET">
{{ form_widget(form) }}
</form>
</div>
<div class="modal-footer">
<a href="{{ path('admin_site_page_index', {page_filter: 0}) }}" class="btn btn-secondary">{{ 'Vider'|trans }}</a>
<button type="submit" form="form-filters" class="btn btn-primary">{{ 'Filter'|trans }}</button>
</div>
</div>
</div>

View File

@ -0,0 +1,67 @@
{% extends '@Core/admin/layout.html.twig' %}
{% block title %}{{ 'Settings'|trans }} - {{ parent() }}{% endblock %}
{% block body %}
<div class="bg-light pl-5 pr-4 pt-5 {% if pager.paginationData.pageCount < 2 %}pb-5{% endif %}">
<div class="d-flex">
<div class="mr-auto w-50">
<h1 class="display-5">{{ 'Settings'|trans }}</h1>
</div>
</div>
{{ knp_pagination_render(pager) }}
</div>
<div class="table-responsive">
<table class="table">
<thead class="thead-light">
<tr>
<th class="col-5">{{ 'Label'|trans }}</th>
<th class="col-5">{{ 'Section'|trans }}</th>
<th class="col-2 miw-100 text-right">{{ 'Actions'|trans }}</th>
</tr>
</thead>
<tbody>
{% for item in pager %}
{% set edit = path('admin_setting_edit', {entity: item.id}) %}
<tr data-dblclick="{{ edit }}">
<td class="col-5">
<a href="{{ edit }}" class="font-weight-bold text-body d-block">
{{ item.label }}
</a>
</td>
<td class="col-5">
{{ item.section }}
</td>
<td class="col-2 miw-100 text-right">
<span data-modal="{{ edit }}" class="btn btn-sm btn-primary mr-1">
<span data-modal="{{ edit }}" class="fa fa-edit"></span>
</span>
<button type="submit" form="form-delete-{{ item.id }}" class="btn btn-sm btn-danger">
<span class="fa fa-trash"></span>
</button>
<form method="post" action="{{ path('admin_setting_delete', {entity: item.id}) }}" id="form-delete-{{ item.id }}" data-form-confirm>
<input type="hidden" name="_method" value="DELETE">
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ item.id) }}">
</form>
</td>
</tr>
{% else %}
<tr>
<td class="col-12 text-center p-4 text-black-50" colspan="3">
<div class="display-1">
<span class="fa fa-search"></span>
</div>
<div class="display-5 mt-3">
{{ 'No result'|trans }}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View File

@ -0,0 +1,73 @@
<?php
namespace App\Core\Setting;
use App\Core\Factory\SettingFactory;
use App\Core\Manager\EntityManager;
use App\Core\Repository\SettingRepositoryQuery;
use App\Core\Entity\Setting;
/**
* class SettingManager.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class SettingManager
{
protected EntityManager $entityManager;
protected SettingRepositoryQuery $query;
protected SettingFactory $factory;
public function __construct(EntityManager $entityManager, SettingRepositoryQuery $query, SettingFactory $factory)
{
$this->entityManager = $entityManager;
$this->query = $query;
$this->factory = $factory;
}
public function init(string $code, string $section, string $label, $value = null)
{
$entity = $this->get($code);
$isNew = null === $entity;
if ($isNew) {
$entity = $this->factory->create($code);
$entity->setValue($value);
}
$entity
->setCode($code)
->setSection($section)
->setLabel($label)
;
if ($isNew) {
$this->entityManager->create($entity);
} else {
$this->entityManager->update($entity);
}
}
public function get(string $code): ?Setting
{
return $this->query->create()
->where('.code = :code')
->setParameter(':code', $code)
->findOne()
;
}
public function set(string $code, $value): bool
{
$entity = $this->get($code);
if (!$entity) {
return false;
}
$entity->setValue($value);
$this->entityManager->update($entity);
return true;
}
}

0
src/Event/.gitkeep Normal file
View File

View File

@ -0,0 +1,58 @@
<?php
namespace App\EventSuscriber;
use App\Core\Event\Setting\SettingEvent;
use App\Core\EventSuscriber\SettingEventSubscriber as EventSubscriber;
use App\Core\Setting\SettingManager;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
/**
* class SettingEventSubscriber.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class SettingEventSubscriber extends EventSubscriber
{
protected SettingManager $manager;
public function __construct(SettingManager $manager)
{
$this->manager = $manager;
}
public function onInit(SettingEvent $event)
{
// $this->manager->init('myapp_foo', 'My app', 'Foo', 'Default value');
// $this->manager->init('myapp_bar', 'My app', 'Bar', true);
}
public function onFormInit(SettingEvent $event)
{
$data = $event->getData();
$builder = $data['builder'];
$entity = $data['entity'];
// if ('myapp_foo' === $entity->getCode()) {
// $builder->add(
// 'value',
// TextType::class,
// [
// 'label' => $entity->getLabel(),
// ]
// );
// }
//
// if ('myapp_bar' === $entity->getCode()) {
// $builder->add(
// 'value',
// CheckboxType::class,
// [
// 'label' => $entity->getLabel(),
// 'required' => false,
// ]
// );
// }
}
}