CRUD for domains & playing with symfony stimulus
This commit is contained in:
parent
3ed53ac05e
commit
39e94d8bdc
@ -11,6 +11,11 @@ import { Controller } from '@hotwired/stimulus';
|
|||||||
*/
|
*/
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
connect() {
|
connect() {
|
||||||
this.element.textContent = 'Hello Stimulus! Edit me in assets/controllers/hello_controller.js';
|
//this.element.textContent = 'Hello Stimulus! Edit me in assets/controllers/domains_controller.js';
|
||||||
|
}
|
||||||
|
|
||||||
|
static targets = ['name', 'output']
|
||||||
|
greet() {
|
||||||
|
this.outputTarget.textContent = `Hello, ${this.nameTarget.value}!`
|
||||||
}
|
}
|
||||||
}
|
}
|
85
src/Controller/DomainController.php
Normal file
85
src/Controller/DomainController.php
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Entity\Domain;
|
||||||
|
use App\Form\DomainType;
|
||||||
|
use App\Repository\DomainRepository;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||||
|
use Symfony\Component\String\ByteString;
|
||||||
|
|
||||||
|
#[Route('/domain')]
|
||||||
|
#[IsGranted('IS_AUTHENTICATED_FULLY')]
|
||||||
|
class DomainController extends AbstractController
|
||||||
|
{
|
||||||
|
#[Route('/', name: 'app_domain_index', methods: ['GET'])]
|
||||||
|
public function index(DomainRepository $domainRepository): Response
|
||||||
|
{
|
||||||
|
return $this->render('domain/index.html.twig', [
|
||||||
|
'domains' => $domainRepository->findAll(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/new', name: 'app_domain_new', methods: ['GET', 'POST'])]
|
||||||
|
public function new(Request $request, EntityManagerInterface $entityManager): Response
|
||||||
|
{
|
||||||
|
$domain = new Domain();
|
||||||
|
$domain->setOwnerToken(ByteString::fromRandom(64)->toString());
|
||||||
|
$form = $this->createForm(DomainType::class, $domain);
|
||||||
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
$entityManager->persist($domain);
|
||||||
|
$entityManager->flush();
|
||||||
|
|
||||||
|
return $this->redirectToRoute('app_domain_index', [], Response::HTTP_SEE_OTHER);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('domain/new.html.twig', [
|
||||||
|
'domain' => $domain,
|
||||||
|
'form' => $form,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/{id}', name: 'app_domain_show', methods: ['GET'])]
|
||||||
|
public function show(Domain $domain): Response
|
||||||
|
{
|
||||||
|
return $this->render('domain/show.html.twig', [
|
||||||
|
'domain' => $domain,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/{id}/edit', name: 'app_domain_edit', methods: ['GET', 'POST'])]
|
||||||
|
public function edit(Request $request, Domain $domain, EntityManagerInterface $entityManager): Response
|
||||||
|
{
|
||||||
|
$form = $this->createForm(DomainType::class, $domain);
|
||||||
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
$entityManager->flush();
|
||||||
|
|
||||||
|
return $this->redirectToRoute('app_domain_index', [], Response::HTTP_SEE_OTHER);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('domain/edit.html.twig', [
|
||||||
|
'domain' => $domain,
|
||||||
|
'form' => $form,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/{id}', name: 'app_domain_delete', methods: ['POST'])]
|
||||||
|
public function delete(Request $request, Domain $domain, EntityManagerInterface $entityManager): Response
|
||||||
|
{
|
||||||
|
if ($this->isCsrfTokenValid('delete'.$domain->getId(), $request->request->get('_token'))) {
|
||||||
|
$entityManager->remove($domain);
|
||||||
|
$entityManager->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->redirectToRoute('app_domain_index', [], Response::HTTP_SEE_OTHER);
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,13 @@ use Gedmo\Mapping\Annotation\Timestampable;
|
|||||||
#[ORM\Entity(repositoryClass: DomainRepository::class)]
|
#[ORM\Entity(repositoryClass: DomainRepository::class)]
|
||||||
class Domain
|
class Domain
|
||||||
{
|
{
|
||||||
|
const SORT_POLICIES = [
|
||||||
|
'Recent First' => 'created:desc',
|
||||||
|
'Oldest First' => 'created:asc',
|
||||||
|
'Rating Desc' => 'rating:desc',
|
||||||
|
'Rating Asc' => 'rating:asc',
|
||||||
|
];
|
||||||
|
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\GeneratedValue]
|
#[ORM\GeneratedValue]
|
||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
@ -41,15 +48,11 @@ class Domain
|
|||||||
#[Timestampable(on: "create")]
|
#[Timestampable(on: "create")]
|
||||||
private ?DateTimeInterface $createdAt = null;
|
private ?DateTimeInterface $createdAt = null;
|
||||||
|
|
||||||
#[ORM\OneToMany(mappedBy: 'domain', targetEntity: Comment::class)]
|
|
||||||
private Collection $comments;
|
|
||||||
|
|
||||||
#[ORM\OneToMany(mappedBy: 'domain', targetEntity: DomainPage::class)]
|
#[ORM\OneToMany(mappedBy: 'domain', targetEntity: DomainPage::class)]
|
||||||
private Collection $domainPages;
|
private Collection $domainPages;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->comments = new ArrayCollection();
|
|
||||||
$this->domainPages = new ArrayCollection();
|
$this->domainPages = new ArrayCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,6 +114,15 @@ class Domain
|
|||||||
return $this->defaultSortPolicy;
|
return $this->defaultSortPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDefaultSortPolicyName(): ?string
|
||||||
|
{
|
||||||
|
if(!$this->getDefaultSortPolicy()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_flip(Domain::SORT_POLICIES)[$this->getDefaultSortPolicy()] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
public function setDefaultSortPolicy(string $defaultSortPolicy): static
|
public function setDefaultSortPolicy(string $defaultSortPolicy): static
|
||||||
{
|
{
|
||||||
$this->defaultSortPolicy = $defaultSortPolicy;
|
$this->defaultSortPolicy = $defaultSortPolicy;
|
||||||
@ -142,36 +154,6 @@ class Domain
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Collection<int, Comment>
|
|
||||||
*/
|
|
||||||
public function getComments(): Collection
|
|
||||||
{
|
|
||||||
return $this->comments;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addComment(Comment $comment): static
|
|
||||||
{
|
|
||||||
if (!$this->comments->contains($comment)) {
|
|
||||||
$this->comments->add($comment);
|
|
||||||
$comment->setDomain($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function removeComment(Comment $comment): static
|
|
||||||
{
|
|
||||||
if ($this->comments->removeElement($comment)) {
|
|
||||||
// set the owning side to null (unless already changed)
|
|
||||||
if ($comment->getDomain() === $this) {
|
|
||||||
$comment->setDomain(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Collection<int, DomainPage>
|
* @return Collection<int, DomainPage>
|
||||||
*/
|
*/
|
||||||
|
43
src/Form/DomainType.php
Normal file
43
src/Form/DomainType.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form;
|
||||||
|
|
||||||
|
use App\Entity\Domain;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
class DomainType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('enabled', CheckboxType::class, [
|
||||||
|
'attr' => ['checked' => 'checked', 'value' => '1']
|
||||||
|
])
|
||||||
|
->add('domain', TextType::class, [
|
||||||
|
'attr' => [
|
||||||
|
'placeholder' => 'example.com'
|
||||||
|
]
|
||||||
|
])
|
||||||
|
->add('name', TextType::class, [
|
||||||
|
'attr' => [
|
||||||
|
'placeholder' => "John Doe's Blog"
|
||||||
|
]
|
||||||
|
])
|
||||||
|
->add('defaultSortPolicy', ChoiceType::class, [
|
||||||
|
'choices' => Domain::SORT_POLICIES,
|
||||||
|
])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => Domain::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -224,7 +224,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"assets/bootstrap.js",
|
"assets/bootstrap.js",
|
||||||
"assets/controllers.json",
|
"assets/controllers.json",
|
||||||
"assets/controllers/hello_controller.js"
|
"assets/controllers/domains_controller.js"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"symfony/translation": {
|
"symfony/translation": {
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
<i class="fas fa-comments fa-fw me-3"></i>
|
<i class="fas fa-comments fa-fw me-3"></i>
|
||||||
<span>Comments</span>
|
<span>Comments</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="#" class="list-group-item list-group-item-action py-2 ripple">
|
<a href="{{ path('app_domain_index') }}" class="list-group-item list-group-item-action py-2 ripple">
|
||||||
<i class="fas fa-globe fa-fw me-3"></i>
|
<i class="fas fa-globe fa-fw me-3"></i>
|
||||||
<span>Domains</span>
|
<span>Domains</span>
|
||||||
</a>
|
</a>
|
||||||
|
4
templates/domain/_delete_form.html.twig
Normal file
4
templates/domain/_delete_form.html.twig
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<form method="post" action="{{ path('app_domain_delete', {'id': domain.id}) }}" onsubmit="return confirm('Are you sure you want to delete this item?');">
|
||||||
|
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ domain.id) }}">
|
||||||
|
<button class="btn">Delete</button>
|
||||||
|
</form>
|
4
templates/domain/_form.html.twig
Normal file
4
templates/domain/_form.html.twig
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{{ form_start(form) }}
|
||||||
|
{{ form_widget(form) }}
|
||||||
|
<button class="btn">{{ button_label|default('Save') }}</button>
|
||||||
|
{{ form_end(form) }}
|
13
templates/domain/edit.html.twig
Normal file
13
templates/domain/edit.html.twig
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block title %}Edit Domain{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<h1>Edit Domain</h1>
|
||||||
|
|
||||||
|
{{ include('domain/_form.html.twig', {'button_label': 'Update'}) }}
|
||||||
|
|
||||||
|
<a href="{{ path('app_domain_index') }}">back to list</a>
|
||||||
|
|
||||||
|
{{ include('domain/_delete_form.html.twig') }}
|
||||||
|
{% endblock %}
|
43
templates/domain/index.html.twig
Normal file
43
templates/domain/index.html.twig
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block title %}Domain List{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<h1>Domain List</h1>
|
||||||
|
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Domain</th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Enabled</th>
|
||||||
|
<th>DefaultSortPolicy</th>
|
||||||
|
<th>UpdatedAt</th>
|
||||||
|
<th>CreatedAt</th>
|
||||||
|
<th>actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for domain in domains %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ domain.domain }}</td>
|
||||||
|
<td>{{ domain.name }}</td>
|
||||||
|
<td>{{ domain.enabled ? 'Yes' : 'No' }}</td>
|
||||||
|
<td>{{ domain.getDefaultSortPolicyName }}</td>
|
||||||
|
<td>{{ domain.updatedAt ? domain.updatedAt|date('Y-m-d H:i:s') : '' }}</td>
|
||||||
|
<td>{{ domain.createdAt ? domain.createdAt|date('Y-m-d H:i:s') : '' }}</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ path('app_domain_show', {'id': domain.id}) }}">show</a>
|
||||||
|
<a href="{{ path('app_domain_edit', {'id': domain.id}) }}">edit</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="9">no records found</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<a href="{{ path('app_domain_new') }}">Create new</a>
|
||||||
|
{% endblock %}
|
11
templates/domain/new.html.twig
Normal file
11
templates/domain/new.html.twig
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block title %}New Domain{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<h1>Create new Domain</h1>
|
||||||
|
|
||||||
|
{{ include('domain/_form.html.twig') }}
|
||||||
|
|
||||||
|
<a href="{{ path('app_domain_index') }}">back to list</a>
|
||||||
|
{% endblock %}
|
50
templates/domain/show.html.twig
Normal file
50
templates/domain/show.html.twig
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block title %}Domain{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<h1>Domain</h1>
|
||||||
|
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>Id</th>
|
||||||
|
<td>{{ domain.id }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Domain</th>
|
||||||
|
<td>{{ domain.domain }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>OwnerToken</th>
|
||||||
|
<td>{{ domain.ownerToken }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<td>{{ domain.name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Enabled</th>
|
||||||
|
<td>{{ domain.enabled ? 'Yes' : 'No' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>DefaultSortPolicy</th>
|
||||||
|
<td>{{ domain.getDefaultSortPolicyName }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>UpdatedAt</th>
|
||||||
|
<td>{{ domain.updatedAt ? domain.updatedAt|date('Y-m-d H:i:s') : '' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>CreatedAt</th>
|
||||||
|
<td>{{ domain.createdAt ? domain.createdAt|date('Y-m-d H:i:s') : '' }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<a href="{{ path('app_domain_index') }}">back to list</a>
|
||||||
|
|
||||||
|
<a href="{{ path('app_domain_edit', {'id': domain.id}) }}">edit</a>
|
||||||
|
|
||||||
|
{{ include('domain/_delete_form.html.twig') }}
|
||||||
|
{% endblock %}
|
Loading…
x
Reference in New Issue
Block a user