add projects

This commit is contained in:
Simon Vieille 2022-04-05 12:05:06 +02:00
parent cf5c37975e
commit 1717ce5c2c
13 changed files with 610 additions and 2 deletions

View file

@ -18,11 +18,13 @@ core:
- {name: 'Blog\PostFollowController::enable', action: 'App\Controller\Blog\PostFollowController::enable'}
- {name: 'Blog\PostFollowController::disable', action: 'App\Controller\Blog\PostFollowController::disable'}
- {name: 'StlMeshController::meshes', action: 'App\Controller\StlMeshController::meshes'}
- {name: 'ProjectController::projects', action: 'App\Controller\ProjectController::projects'}
pages:
App\Entity\Page\SimplePage:
name: 'Page de contenu'
templates:
- {name: "Par défaut", file: "page/simple/default.html.twig"}
- {name: "Projets", file: "page/simple/projects.html.twig"}
App\Entity\Page\PostPage:
name: 'Page article'
templates:

View file

@ -0,0 +1,149 @@
<?php
namespace App\Controller;
use App\Core\Controller\Admin\Crud\CrudController;
use App\Core\Crud\CrudConfiguration;
use App\Core\Crud\Field;
use App\Core\Entity\EntityInterface;
use App\Core\Manager\EntityManager;
use App\Entity\Project as Entity;
use App\Factory\ProjectFactory as Factory;
use App\Form\ProjectType as Type;
use App\Repository\ProjectRepositoryQuery as RepositoryQuery;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Routing\Annotation\Route;
use App\Entity\Project;
class ProjectAdminController extends CrudController
{
/**
* @Route("/admin/project/{page}", name="admin_project_index", methods={"GET"}, requirements={"page":"\d+"})
*/
public function index(RepositoryQuery $query, Request $request, Session $session, int $page = 1): Response
{
return $this->doIndex($page, $query, $request, $session);
}
/**
* @Route("/admin/project/new", name="admin_project_new", methods={"GET", "POST"})
*/
public function new(Factory $factory, EntityManager $entityManager, Request $request): Response
{
return $this->doNew($factory->create(), $entityManager, $request);
}
/**
* @Route("/admin/project/show/{entity}", name="admin_project_show", methods={"GET"})
*/
public function show(Entity $entity): Response
{
return $this->doShow($entity);
}
/**
* @Route("/admin/project/filter", name="admin_project_filter", methods={"GET"})
*/
public function filter(Session $session): Response
{
return $this->doFilter($session);
}
/**
* @Route("/admin/project/edit/{entity}", name="admin_project_edit", methods={"GET", "POST"})
*/
public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response
{
return $this->doEdit($entity, $entityManager, $request);
}
/**
* @Route("/admin/project/sort/{page}", name="admin_project_sort", methods={"POST"}, requirements={"page":"\d+"})
*/
public function sort(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response
{
return $this->doSort($page, $query, $entityManager, $request, $session);
}
/**
* @Route("/admin/project/batch/{page}", name="admin_project_batch", methods={"POST"}, requirements={"page":"\d+"})
*/
public function batch(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response
{
return $this->doBatch($page, $query, $entityManager, $request, $session);
}
/**
* @Route("/admin/project/delete/{entity}", name="admin_project_delete", methods={"DELETE"})
*/
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
{
return $this->doDelete($entity, $entityManager, $request);
}
protected function getConfiguration(): CrudConfiguration
{
return CrudConfiguration::create()
->setPageTitle('index', 'Projets')
->setPageTitle('edit', '{label}')
->setPageTitle('new', 'Nouveau projet')
// ->setPageTitle('show', 'View of {id}')
->setPageRoute('index', 'admin_project_index')
->setPageRoute('new', 'admin_project_new')
->setPageRoute('edit', 'admin_project_edit')
// ->setPageRoute('show', 'admin_project_show')
->setPageRoute('sort', 'admin_project_sort')
->setPageRoute('batch', 'admin_project_batch')
->setPageRoute('delete', 'admin_project_delete')
->setPageRoute('filter', 'admin_project_filter')
->setForm('edit', Type::class, [])
->setForm('new', Type::class)
// ->setForm('filter', Type::class)
// ->setMaxPerPage('index', 20)
->setIsSortableCollection('index', true)
// ->setSortableCollectionProperty('sortOrder')
// ->setAction('index', 'new', true)
->setAction('index', 'show', false)
// ->setAction('index', 'edit', true)
// ->setAction('index', 'delete', true)
// ->setAction('edit', 'back', true)
->setAction('edit', 'show', false)
// ->setAction('edit', 'delete', true)
// ->setAction('show', 'back', true)
// ->setAction('show', 'edit', true)
->setField('index', 'Label', Field\TextField::class, [
'property' => 'label',
])
->setField('index', 'Status', Field\TextField::class, [
'view' => 'blog/project_admin/field/status.html.twig',
'sort' => ['status', '.status'],
'attr' => ['class' => 'miw-100'],
])
->setBatchAction('index', 'delete', 'Delete', function(EntityInterface $entity, EntityManager $manager) {
$manager->delete($entity);
})
->setBatchAction('index', 'draft', 'Statut : publier', function(EntityInterface $entity, EntityManager $manager) {
$manager->update($entity->setStatus(Project::PUBLISHED));
})
->setBatchAction('index', 'publish', 'Statut : brouillon', function(EntityInterface $entity, EntityManager $manager) {
$manager->update($entity->setStatus(Project::DRAFT));
})
;
}
protected function getSection(): string
{
return 'project';
}
}

View file

@ -0,0 +1,17 @@
<?php
namespace App\Controller;
use App\Core\Controller\Site\PageController;
use App\Repository\ProjectRepositoryQuery;
use Symfony\Component\HttpFoundation\Response;
class ProjectController extends PageController
{
public function projects(ProjectRepositoryQuery $query): Response
{
return $this->defaultRender($this->siteRequest->getPage()->getTemplate(), [
'projects' => $query->create()->published()->ordered()->find(),
]);
}
}

117
src/Entity/Project.php Normal file
View file

@ -0,0 +1,117 @@
<?php
namespace App\Entity;
use App\Core\Doctrine\Timestampable;
use App\Core\Entity\EntityInterface;
use App\Repository\ProjectRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=ProjectRepository::class)
* @ORM\HasLifecycleCallbacks
*/
class Project implements EntityInterface
{
use Timestampable;
const DRAFT = 0;
const PUBLISHED = 1;
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $label;
/**
* @ORM\Column(type="text", nullable=true)
*/
private $description;
/**
* @ORM\Column(type="integer", options={"default"=0})
*/
private $status = 0;
/**
* @ORM\Column(type="integer", nullable=true)
*/
private $sortOrder;
/**
* @ORM\Column(type="array")
*/
private $links = [];
public function getId(): ?int
{
return $this->id;
}
public function getLabel(): ?string
{
return $this->label;
}
public function setLabel(string $label): self
{
$this->label = $label;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
public function getStatus(): ?int
{
return $this->status;
}
public function setStatus(int $status): self
{
$this->status = $status;
return $this;
}
public function getSortOrder(): ?int
{
return $this->sortOrder;
}
public function setSortOrder(?int $sortOrder): self
{
$this->sortOrder = $sortOrder;
return $this;
}
public function getLinks(): ?array
{
return $this->links;
}
public function setLinks(array $links): self
{
$this->links = $links;
return $this;
}
}

View file

@ -0,0 +1,14 @@
<?php
namespace App\Factory;
use App\Core\Factory\FactoryInterface;
use App\Entity\Project as Entity;
class ProjectFactory implements FactoryInterface
{
public function create(): Entity
{
return new Entity();
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Form\Extension\Core\Type\TextType;
class ProjectLinkType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add(
'label',
TextType::class,
[
'label' => 'Libellé',
'required' => true,
'attr' => [
],
'constraints' => [
new NotBlank(),
],
]
);
$builder->add(
'url',
TextType::class,
[
'label' => 'URL',
'required' => true,
'attr' => [
],
'constraints' => [
new NotBlank(),
],
]
);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
// Configure your form options here
]);
}
}

82
src/Form/ProjectType.php Normal file
View file

@ -0,0 +1,82 @@
<?php
namespace App\Form;
use App\Entity\Project;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;
use App\Form\Type\SimpleMdTextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use App\Core\Form\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
class ProjectType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add(
'label',
TextType::class,
[
'label' => 'Libellé',
'required' => true,
'attr' => [
],
'constraints' => [
new NotBlank(),
],
]
);
$builder->add(
'status',
ChoiceType::class,
[
'label' => 'Statut',
'required' => true,
'choices' => [
'Brouillon' => Project::DRAFT,
'Publié' => Project::PUBLISHED,
],
'attr' => [
],
'constraints' => [
new NotBlank(),
],
]
);
$builder->add(
'description',
SimpleMdTextareaType::class,
[
'label' => 'Contenu',
'required' => false,
'constraints' => [
],
]
);
$builder->add(
'links',
CollectionType::class,
[
'label' => 'Liens',
'entry_type' => ProjectLinkType::class,
'by_reference' => false,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
]
);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Project::class,
]);
}
}

View file

@ -0,0 +1,76 @@
<?php
namespace App\Repository;
use App\Entity\Project;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\OptimisticLockException;
use Doctrine\ORM\ORMException;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method Project|null find($id, $lockMode = null, $lockVersion = null)
* @method Project|null findOneBy(array $criteria, array $orderBy = null)
* @method Project[] findAll()
* @method Project[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ProjectRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Project::class);
}
/**
* @throws ORMException
* @throws OptimisticLockException
*/
public function add(Project $entity, bool $flush = true): void
{
$this->_em->persist($entity);
if ($flush) {
$this->_em->flush();
}
}
/**
* @throws ORMException
* @throws OptimisticLockException
*/
public function remove(Project $entity, bool $flush = true): void
{
$this->_em->remove($entity);
if ($flush) {
$this->_em->flush();
}
}
// /**
// * @return Project[] Returns an array of Project objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('p')
->andWhere('p.exampleField = :val')
->setParameter('val', $value)
->orderBy('p.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?Project
{
return $this->createQueryBuilder('p')
->andWhere('p.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}

View file

@ -0,0 +1,29 @@
<?php
namespace App\Repository;
use App\Core\Repository\RepositoryQuery;
use Knp\Component\Pager\PaginatorInterface;
use App\Repository\ProjectRepository as Repository;
use App\Entity\Project;
class ProjectRepositoryQuery extends RepositoryQuery
{
public function __construct(Repository $repository, PaginatorInterface $paginator)
{
parent::__construct($repository, 'p', $paginator);
}
public function published()
{
return $this
->andWhere('.status = :published')
->setParameter(':published', Project::PUBLISHED)
;
}
public function ordered()
{
return $this->orderBy('.sortOrder');
}
}

View file

@ -25,12 +25,21 @@
}) }}
</ul>
{{ include('@Core/admin/module/_menu_section.html.twig', {label: 'Impression 3D'}) }}
{{ include('@Core/admin/module/_menu_section.html.twig', {label: 'Projets'}) }}
<ul class="nav flex-column">
{{ include('@Core/admin/module/_menu_item.html.twig', {
id: 'project',
label: 'Projets',
route: path('admin_project_index'),
icon: 'fa fa-hat-wizard'
}) }}
</ul>
<ul class="nav flex-column">
{{ include('@Core/admin/module/_menu_item.html.twig', {
id: 'stl_mesh',
label: 'Mesh',
label: 'Modèles 3D',
route: path('admin_stl_mesh_index'),
icon: 'fa fa-pen'
}) }}

View file

@ -0,0 +1,7 @@
{% set map = {
0: ['warning', 'Brouillon'],
1: ['success', 'Publié'],
} %}
<button class="btn btn-sm btn-{{ map[entity.status].0 }}">
{{ map[entity.status].1 }}
</button>

View file

@ -0,0 +1,20 @@
{% if entity.image %}
{% set image = asset(entity.image.pathname) %}
{% else %}
{% set image = asset('build/images/no-image.png') %}
{% endif %}
<img src="{{ asset('build/images/blank.png') }}" style="background: url({{ image }}) center center; background-size: cover" class="rounded float-left mr-2">
<a href="{{ path('admin_blog_post_show', {entity: entity.id}) }}" class="font-weight-bold text-body d-block">
{{ entity.title }}
</a>
{% set categories = [] %}
{% for category in entity.categories %}
{% set url = path('admin_blog_category_show', {entity: category.id}) %}
{% set categories = categories|merge(['<a href="' ~ url ~ '">' ~ category.title ~ '</a>']) %}
{% endfor %}
Dans {{ categories|join(', ')|raw }}

View file

@ -0,0 +1,36 @@
{% extends 'base.html.twig' %}
{% block page_title %}
{{- _page.title.value -}}
{% endblock %}
{% block body %}
<div class="col-12">
<div class="body">
<div class="body-content">
{{- _page.content.value|murph_url|markdown('post') -}}
{% for project in projects %}
<hr>
<h2>{{- project.label -}}</h2>
{{- project.description|murph_url|markdown('post') -}}
{% if project.links %}
<ul class="list--inline">
{% for link in project.links %}
<li>
<a class="button small" href="{{ link.url|murph_url }}" target="_blank">
{{- link.label -}}
</a>
</li>
{% endfor %}
</ul>
{% endif %}
{% endfor %}
</div>
</div>
</div>
{% endblock %}