Browse Source

Sortable sur entités / hierachy des entités sur ProductCategory

reduction
Fab 4 years ago
parent
commit
5667261404
17 changed files with 485 additions and 63 deletions
  1. +2
    -1
      README.md
  2. +128
    -31
      ShopBundle/Controller/Admin/AdminController.php
  3. +13
    -11
      ShopBundle/Controller/Admin/MerchantController.php
  4. +27
    -0
      ShopBundle/Form/AbstractEditPositionType.php
  5. +2
    -17
      ShopBundle/Model/AbstractDocumentEntity.php
  6. +3
    -0
      ShopBundle/Model/ProductCategory.php
  7. +33
    -0
      ShopBundle/Model/SortableTrait.php
  8. +8
    -1
      ShopBundle/Resources/config/easy_admin/base.yaml
  9. +4
    -1
      ShopBundle/Resources/public/css/backend/custom.css
  10. +28
    -0
      ShopBundle/Resources/public/js/backend/custom.js
  11. +13
    -0
      ShopBundle/Resources/public/js/backend/jquery-ui.min.js
  12. +5
    -1
      ShopBundle/Resources/public/js/backend/setup-ckfinder.js
  13. +96
    -0
      ShopBundle/Resources/public/js/backend/utils.js
  14. +1
    -0
      ShopBundle/Resources/translations/EasyAdminBundle.fr.yaml
  15. +26
    -0
      ShopBundle/Resources/views/backend/default/list.html.twig
  16. +3
    -0
      ShopBundle/Resources/views/backend/default/list_children.html.twig
  17. +93
    -0
      ShopBundle/Resources/views/backend/default/sortable.html.twig

+ 2
- 1
README.md View File

@@ -1 +1,2 @@
#Laclic LcShopBundle
#Laclic LcShopBundle


+ 128
- 31
ShopBundle/Controller/Admin/AdminController.php View File

@@ -3,8 +3,11 @@
namespace Lc\ShopBundle\Controller\Admin;

use EasyCorp\Bundle\EasyAdminBundle\Controller\EasyAdminController;
use EasyCorp\Bundle\EasyAdminBundle\Event\EasyAdminEvents;
use FOS\UserBundle\Model\UserManagerInterface;
use Lc\ShopBundle\Context\MerchantInterface;
use Lc\ShopBundle\Form\AbstractEditPositionType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Security\Core\Security;

class AdminController extends EasyAdminController
@@ -15,7 +18,7 @@ class AdminController extends EasyAdminController
public function __construct(Security $security, UserManagerInterface $userManager)
{
$this->security = $security;
$this->userManager = $userManager ;
$this->userManager = $userManager;
}

protected function createListQueryBuilder($entityClass, $sortDirection, $sortField = null, $dqlFilter = null)
@@ -26,12 +29,12 @@ class AdminController extends EasyAdminController

$queryBuilder = parent::createListQueryBuilder($entityClass, $sortDirection, $sortField, $dqlFilter);

if($entityClass == 'App\Entity\PointSale') {
if ($entityClass == 'App\Entity\PointSale') {
$queryBuilder->andWhere(':currentMerchant MEMBER OF entity.merchant')
->setParameter(':currentMerchant',$this->getUser()->getMerchant()->getId()) ;
->setParameter(':currentMerchant', $this->getUser()->getMerchant()->getId());
}

return $queryBuilder ;
return $queryBuilder;
}


@@ -40,22 +43,22 @@ class AdminController extends EasyAdminController
$parameters = array_merge(
$parameters,
$this->getTwigExtraParameters()
) ;
);

return parent::renderTemplate($actionName, $templatePath, $parameters) ;
return parent::renderTemplate($actionName, $templatePath, $parameters);
}

public function getTwigExtraParameters()
{
$repositoryMerchant = $this->getDoctrine()->getRepository(MerchantInterface::class);
$merchants = $repositoryMerchant->findAll() ;
$merchants = $repositoryMerchant->findAll();

$user = $this->security->getUser() ;
$user = $this->security->getUser();

return [
'merchants' => $merchants,
'current_merchant' => ($user && $user->getMerchant()) ? $user->getMerchant() : null,
] ;
];
}

public function updateEntity($entity)
@@ -67,41 +70,50 @@ class AdminController extends EasyAdminController
public function persistEntity($entity)
{

if (method_exists($entity, 'setPosition')) {
//Product Category avec parent
if (method_exists($entity, 'getProductCategory') && $entity->getProductCategory()) {

$total = $this->em->getRepository($this->entity['class'])->count(array('status' => 1, 'productCategory' => $entity->getProductCategory()->getId()));
$entity->setPosition($entity->getProductCategory()->getId() . '_' . $total);

//set position
}else{
$total = $this->em->getRepository($this->entity['class'])->count(array('status' => 1, 'productCategory' => null ));
$entity->setPosition($total);
}
}
if (method_exists($entity, 'setMerchant')) {
$entity->setMerchant($this->security->getUser()->getMerchant());
}

if (method_exists($entity, 'addMerchant')) {
if($entity->getMerchant()->isEmpty()) {
if ($entity->getMerchant()->isEmpty()) {
$entity->addMerchant($this->security->getUser()->getMerchant());
}
}

if (method_exists($entity, 'setCreatedAt')) {
$entity->setCreatedAt(new \DateTime());
}

if (method_exists($entity, 'setCreatedAt')) {
if (method_exists($entity, 'setCreatedBy')) {
$entity->setCreatedBy($this->security->getUser());
}

if (method_exists($entity, 'getAddress')) {
$entity->getAddress()->setCreatedBy($this->security->getUser());
$entity->getAddress()->setCreatedAt(new \DateTime());
}

if (method_exists($entity, 'getAddresses')) {
foreach($entity->getAddresses() as $address) {
$address->setCreatedBy($this->security->getUser()) ;
$address->setCreatedAt(new \DateTime()) ;
}
}
//ça bloque tout ce truc là faut qu'on en parle

$this->setUpdated($entity);
/* if (method_exists($entity, 'getAddress')) {
$entity->getAddress()->setCreatedBy($this->security->getUser());
$entity->getAddress()->setCreatedAt(new \DateTime());
}

if (method_exists($entity, 'setPosition')) {
$entity->setPosition(0);
}
if (method_exists($entity, 'getAddresses')) {
foreach($entity->getAddresses() as $address) {
$address->setCreatedBy($this->security->getUser()) ;
$address->setCreatedAt(new \DateTime()) ;
}
}*/

$this->setUpdated($entity);

if (method_exists($entity, 'setStatus')) {
$entity->setStatus(1);
@@ -112,14 +124,13 @@ class AdminController extends EasyAdminController

public function setUpdated($entity)
{
if (method_exists($entity, 'setUpdatedAt')) {
$entity->setUpdatedAt(new \DateTime());
}

if (method_exists($entity, 'setUpdatedBy')) {
$entity->setUpdatedBy($this->security->getUser());
}

//ça bloque tout ce truc là faut qu'on en parle
/*
if (method_exists($entity, 'getAddress')) {
$entity->getAddress()->setUpdatedBy($this->security->getUser());
}
@@ -133,7 +144,93 @@ class AdminController extends EasyAdminController
$address->setCreatedAt(new \DateTime()) ;
}
}
}*/
}

public function listChildrenAction()
{
$this->dispatch(EasyAdminEvents::PRE_LIST);

$id = $this->request->query->get('id');
$this->entity['list']['dql_filter'] = "entity.productCategory = " . $id;

$easyadmin = $this->request->attributes->get('easyadmin');
$entity = $easyadmin['item'];

$fields = $this->entity['list']['fields'];
$paginator = $this->findAll($this->entity['class'], $this->request->query->get('page', 1), $this->entity['list']['max_results'], $this->request->query->get('sortField'), $this->request->query->get('sortDirection'), $this->entity['list']['dql_filter']);

$this->dispatch(EasyAdminEvents::POST_LIST, ['paginator' => $paginator]);

$parameters = [
'paginator' => $paginator,
'fields' => $fields,
'batch_form' => $this->createBatchForm($this->entity['name'])->createView(),
'delete_form_template' => $this->createDeleteForm($this->entity['name'], '__id__')->createView(),
'entity' => $entity,
];

return $this->executeDynamicMethod('render<EntityName>Template', ['list', $this->entity['templates']['list'], $parameters]);
}


public function sortAction()
{

$this->dispatch(EasyAdminEvents::PRE_LIST);
$entity = null;
if($this->request->query->get('id')) {
$id = $this->request->query->get('id');
$this->entity['list']['dql_filter'] = "entity.productCategory = " . $id;

$easyadmin = $this->request->attributes->get('easyadmin');
$entity = $easyadmin['item'];
}
$fields = $this->entity['list']['fields'];
$paginator = $this->findAll($this->entity['class'], $this->request->query->get('page', 1), 500, $this->request->query->get('sortField'), $this->request->query->get('sortDirection'), $this->entity['list']['dql_filter']);

$this->dispatch(EasyAdminEvents::POST_LIST, ['paginator' => $paginator]);

$positionForm =$this->createFormBuilder(array('entities', $paginator->getCurrentPageResults()))
->add('entities', CollectionType::class ,array(
'required' => true,
'allow_add' => true,
'entry_type' => AbstractEditPositionType::class,
))
->getForm();

$positionForm->handleRequest($this->request);

if($positionForm->isSubmitted()&& $positionForm->isValid()){
$class = $this->entity['class'];
$repo = $this->em->getRepository($class);

foreach ($positionForm->get('entities')->getData() as $elm){
$this->dispatch(EasyAdminEvents::PRE_UPDATE, ['entity' => $entity]);
$entity = $repo->find($elm['id']);
$entity->setPosition($elm['position']);
$this->em->persist($entity);
$this->dispatch(EasyAdminEvents::POST_UPDATE, ['entity' => $entity]);

}
$this->em->flush();

$this->addFlash('success','Position modifié', array(), 'mweb');

return $this->redirectToReferrer();

}

$parameters = [
'paginator' => $paginator,
'fields' => $fields,
'batch_form' => $this->createBatchForm($this->entity['name'])->createView(),
'delete_form_template' => $this->createDeleteForm($this->entity['name'], '__id__')->createView(),
'postion_form' => $positionForm->createView(),
'entity' => $entity,
'sortable'=> true
];
return $this->executeDynamicMethod('render<EntityName>Template', ['sortable', "@LcShop/backend/default/sortable.html.twig", $parameters]);
}

}

+ 13
- 11
ShopBundle/Controller/Admin/MerchantController.php View File

@@ -42,18 +42,20 @@ class MerchantController extends AdminController
public function switchMerchantAction(Request $request): RedirectResponse
{
$em = $this->getDoctrine()->getManager();
$idMerchant = $request->request->get('id_merchant') ;
$merchant = $this->getDoctrine()
->getRepository(MerchantInterface::class)
->find($idMerchant);

if($merchant) {
$user = $this->security->getUser() ;
$user->setMerchant($merchant) ;
$em->persist($user);
$em->flush() ;
}
$user = $this->security->getUser() ;
if($user->hasRole('ROLE_SUPER_ADMIN')) {
$idMerchant = $request->request->get('id_merchant');
$merchant = $this->getDoctrine()
->getRepository(MerchantInterface::class)
->find($idMerchant);

if ($merchant) {

$user->setMerchant($merchant);
$em->persist($user);
$em->flush();
}
}
return $this->redirect('admin/dashboard') ;
}


+ 27
- 0
ShopBundle/Form/AbstractEditPositionType.php View File

@@ -0,0 +1,27 @@
<?php

namespace Lc\ShopBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class AbstractEditPositionType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('id', HiddenType::class);
$builder->add('position', HiddenType::class);
}

}

+ 2
- 17
ShopBundle/Model/AbstractDocumentEntity.php View File

@@ -11,6 +11,8 @@ use Lc\ShopBundle\Model\AbstractEntity;
*/
abstract class AbstractDocumentEntity extends AbstractEntity
{
use SortableTrait;

/**
* @ORM\Column(type="string", length=255)
*/
@@ -37,12 +39,6 @@ abstract class AbstractDocumentEntity extends AbstractEntity
*/
protected $status;

/**
* @ORM\Column(type="integer")
* @Gedmo\SortablePosition()
*/
protected $position;


public function getTitle(): ?string
{
@@ -104,15 +100,4 @@ abstract class AbstractDocumentEntity extends AbstractEntity
return $this;
}

public function getPosition(): ?int
{
return $this->position;
}

public function setPosition(int $position): self
{
$this->position = $position;

return $this;
}
}

+ 3
- 0
ShopBundle/Model/ProductCategory.php View File

@@ -5,12 +5,15 @@ namespace Lc\ShopBundle\Model;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Orkestra\EaSortable\SortableTrait;

/**
* @ORM\MappedSuperclass()
*/
abstract class ProductCategory extends AbstractDocumentEntity
{


/**
* @ORM\ManyToOne(targetEntity="Lc\ShopBundle\Context\ProductCategoryInterface", inversedBy="productCategories")
*/

+ 33
- 0
ShopBundle/Model/SortableTrait.php View File

@@ -0,0 +1,33 @@
<?php

namespace Lc\ShopBundle\Model;

use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;

trait SortableTrait
{
/**
* @var string
* @ORM\Column(type="string")
*/
protected $position = 0;

/**
* @return string
*/
public function getPosition(): string
{
return $this->position;
}

/**
* @param string $position
* @return $this
*/
public function setPosition(string $position): self
{
$this->position = $position;
return $this;
}
}

+ 8
- 1
ShopBundle/Resources/config/easy_admin/base.yaml View File

@@ -1,11 +1,16 @@
easy_admin:
design:
templates:
list: '@LcShop/backend/default/list.html.twig'
layout: '@LcShop/backend/default/layout.html.twig'
brand_color: '#1ABC9C'
assets:
favicon: '/assets/img/backend/favicon-pdl.png'
js:
- '/bundles/cksourceckfinder/ckfinder/ckfinder.js'
- '/bundles/lcshop/js/backend/setup-ckfinder.js'
- '/bundles/lcshop/js/backend/jquery-ui.min.js'
- '/bundles/lcshop/js/backend/utils.js'
- '/bundles/lcshop/js/backend/custom.js'
css:
- '/bundles/lcshop/css/backend/custom.css'
@@ -16,7 +21,9 @@ easy_admin:
- '@CKSourceCKFinder/Form/fields.html.twig'
list:
max_results: 30

actions:
- { name: 'edit', icon: 'pencil'}
- { name: 'delete', icon: 'trash'}
formats:
date: 'd/m/Y'
time: 'h:i A e'

+ 4
- 1
ShopBundle/Resources/public/css/backend/custom.css View File

@@ -5,4 +5,7 @@

#switch-merchant select {
width: 100% ;
}
}

.ui-sortable-helper{ display: table;}
.ui-state-highlight{background: #eee}

+ 28
- 0
ShopBundle/Resources/public/js/backend/custom.js View File

@@ -1,10 +1,38 @@

jQuery(document).ready(function() {
custom_switch_merchants() ;
setSortableList();
}) ;

function custom_switch_merchants() {
$('#switch-merchant select').change(function() {
$('#switch-merchant form').submit() ;
}) ;
}

function setSortableList() {

$('.lc-sortable tbody').sortable({
placeholder: "ui-state-highlight"
});

$('.lc-sortable tbody').on("sortupdate", function (event, ui) {

prototype = $('#form_entities').data('prototype');

$('.lc-sortable tr.lc-draggable').each(function (index, li) {
// Replace '__name__' in the prototype's HTML to
// instead be a number based on how many items we have
var newForm = prototype.replace(/__name__/g, index);

$(li).append(newForm);

$(li).find('div:last-child').remove();
$(li).find('#form_entities_' + index + '_id').val($(li).data('id'));
log($('.lc-sortable').data('parent-position')+'_'+index);
$(li).find('#form_entities_' + index + '_position').val($('.lc-sortable').data('parent-position')+'_'+index);
});

});

}

+ 13
- 0
ShopBundle/Resources/public/js/backend/jquery-ui.min.js
File diff suppressed because it is too large
View File


+ 5
- 1
ShopBundle/Resources/public/js/backend/setup-ckfinder.js View File

@@ -1 +1,5 @@
CKFinder.config( { connectorPath: '/ckfinder/connector' } );
$(document).on('ready', function () {
if ($('.field-ckfinder_file_chooser').length > 0) {
CKFinder.config({connectorPath: '/ckfinder/connector'});
}
});

+ 96
- 0
ShopBundle/Resources/public/js/backend/utils.js View File

@@ -0,0 +1,96 @@
/**
* Created by fab on 30/12/17.
*/

/**
* Retourne un prix sans taxe sur base du prix avec tax
*
* @param priceWithTax
* @param taxRate
* @returns {string}
*/
function getPrice(priceWithTax, taxRate) {
return parseFloat(parseFloat(priceWithTax) / (taxRate + 1)).toFixed(2);
}

/**
* Retourne un prix avec taxe sur base du prix sans taxe
*
* @param priceWithoutTax
* @param taxRate
* @returns {string}
*/
function getPriceWithTax(priceWithoutTax, taxRate) {
return parseFloat(parseFloat(priceWithoutTax) * (taxRate + 1)).toFixed(2);
}

/**
* Formate un prix en l'arrondissant et en ajoutant le sigle de la monnaie
*
* @param price
* @returns {string}
*/
function formatPrice(price) {
return Number(price).toFixed(2).replace('.', ',') + ' €';
}

/**
* Formate une date au format jj/mm/yyyy
* @param date
* @returns {*}
*/
function formatDate(date) {
if (date) {
return ('0' + date.getDate()).slice(-2) + '/' + ('0' + (date.getMonth() + 1)).slice(-2) + '/' + date.getFullYear();
}
return false;
}

/**
* Equivalent de console.log (ne déclenche pas d'erreur si la console est fermé)
*
* @param msg
*/

function log(msg) {
try {
console.log(msg);
} catch (e) {

}
}

/**
* Convertit un formulaire ou un objet en JSON (utilisé pour l'envoie de donnée en ajax)
*/
$.fn.serializeObject = function () {
var o = {};
var a = this.serializeArray();
$.each(a, function () {
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};


function getDateFormatted(date) {
var _d = date.getDate(),
d = _d > 9 ? _d : '0' + _d,
_m = date.getMonth() + 1,
m = _m > 9 ? _m : '0' + _m,
formatted = date.getFullYear() + '-' + m + '-' + d;
return formatted;
}

//Affiche une alert au click sur un bouton submit lorsqu'un utilisateur admin tente de modifer un établissement
function userNotAllowToEdit() {
alert('Vous n\'êtes pas autorisé à effectuer cette action');
return false;
}

+ 1
- 0
ShopBundle/Resources/translations/EasyAdminBundle.fr.yaml View File

@@ -0,0 +1 @@
list.page_title: niche

+ 26
- 0
ShopBundle/Resources/views/backend/default/list.html.twig View File

@@ -0,0 +1,26 @@
{% extends '@EasyAdmin/default/list.html.twig' %}

{% block content_title %}
{{ parent() }}
{% if 'action' in app.request.uri and app.request.get('action') == "listChildren" %}
<strong>: {{ entity.title }}</strong>
{% endif %}
{% endblock %}
{% block new_action %}
<div class="button-action">
<a class="{{ _action.css_class|default('') }}"
href="{{ path('easyadmin', _request_parameters|merge({ action: _action.name })) }}"
target="{{ _action.target }}">
{% if _action.icon %}<i class="fa fa-fw fa-{{ _action.icon }}"></i>{% endif %}
{{ _action.label is defined and not _action.label is empty ? _action.label|trans(_trans_parameters) }}
</a>
{% if _entity_config['list']['edit_position'] is defined %}
<a class="{{ _action.css_class|default('') }}"
href="{{ path('easyadmin', _request_parameters|merge({ action: 'sort' })) }}"
target="{{ _action.target }}">
Modifier position
</a>
{% endif %}
</div>

{% endblock %}

+ 3
- 0
ShopBundle/Resources/views/backend/default/list_children.html.twig View File

@@ -0,0 +1,3 @@
{% if 'action' in app.request.uri and app.request.get('action') != "listChildren" %}
{{ include('@EasyAdmin/default/action.html.twig') }}
{% endif %}

+ 93
- 0
ShopBundle/Resources/views/backend/default/sortable.html.twig View File

@@ -0,0 +1,93 @@
{% extends '@EasyAdmin/default/list.html.twig' %}

{% block content_title %}
Édition des positions
{% if entity is defined and entity is not null %}
<strong>: {{ entity.title }}</strong>
{% endif %}
{% endblock %}
{% block batch_actions %}{% endblock %}
{% block global_actions %}{% endblock %}
{% block main %}
{% set _fields_visible_by_user = fields|filter((metadata, field) => easyadmin_is_granted(metadata.permission)) %}
{% set _number_of_hidden_results = 0 %}
{% set _list_item_actions = easyadmin_get_actions_for_list_item(_entity_config.name) %}

{{ form_start(postion_form) }}

<table class="table datagrid lc-sortable" data-parent-position="{{ entity is not null ? entity.position : ''}}">
<thead>
{% block table_head %}
<tr>
<th></th>
{% for field, metadata in _fields_visible_by_user %}
{% set isSortingField = (metadata.property == app.request.get('sortField')) or ('association' == metadata.type and app.request.get('sortField') starts with metadata.property ~ '.') %}
{% set nextSortDirection = isSortingField ? (app.request.get('sortDirection') == 'DESC' ? 'ASC' : 'DESC') : 'DESC' %}
{% set _column_label = metadata.label|trans(_trans_parameters) %}
{% set _column_icon = isSortingField ? (nextSortDirection == 'DESC' ? 'fa-arrow-up' : 'fa-arrow-down') : 'fa-sort' %}

<th class="{{ isSortingField ? 'sorted' }} {{ metadata.virtual ? 'virtual' }} {{ metadata.dataType|lower }} {{ metadata.css_class }}" {{ easyadmin_config('design.rtl') ? 'dir="rtl"' }}>
<span>{{ _column_label|raw }}</span>
</th>
{% endfor %}

</tr>
{% endblock table_head %}
</thead>

<tbody>
{% block table_body %}
{% for item in paginator.currentPageResults %}
{% if not easyadmin_is_granted(_entity_config.list.item_permission, item) %}
{% set _number_of_hidden_results = _number_of_hidden_results + 1 %}
{% else %}
{# the empty string concatenation is needed when the primary key is an object (e.g. an Uuid object) #}
{% set _item_id = '' ~ attribute(item, _entity_config.primary_key_field_name) %}
<tr class="lc-draggable" data-id="{{ _item_id }}">
<td>
<i class="fa fa-fw fa-sort"></i>
</td>

{% for field, metadata in _fields_visible_by_user %}
{% set isSortingField = metadata.property == app.request.get('sortField') %}
{% set _column_label = (metadata.label ?: field|humanize)|trans(_trans_parameters) %}

<td class="{{ isSortingField ? 'sorted' }} {{ metadata.dataType|lower }} {{ metadata.css_class }}" {{ easyadmin_config('design.rtl') ? 'dir="rtl"' }}>
{{ easyadmin_render_field_for_list_view(_entity_config.name, item, metadata) }}
</td>
{% endfor %}

</tr>
{% endif %}
{% else %}
<tr>
<td class="no-results" colspan="{{ _fields_visible_by_user|length + 1 }}">
{{ 'search.no_results'|trans(_trans_parameters, 'EasyAdminBundle') }}
</td>
</tr>
{% endfor %}

{% if _number_of_hidden_results > 0 %}
<tr class="datagrid-row-empty">
<td class="text-center" colspan="{{ _fields_visible_by_user|length + 1 }}">
<span class="datagrid-row-empty-message"><i
class="fa fa-lock mr-1"></i> {{ 'security.list.hidden_results'|trans({}, 'EasyAdminBundle') }}</span>
</td>
</tr>
{% endif %}
{% endblock table_body %}
</tbody>
</table>
<div style="display: none;">
{{ form_row(postion_form.entities) }}

{{ form_rest(postion_form) }}

</div>

<div class="form-actions">
<button type="submit" class="btn btn-primary"> Sauvegarder</button>
</div>
{{ form_end(postion_form) }}

{% endblock main %}

Loading…
Cancel
Save