Skip to content

Commit

Permalink
10 - Add a form to add, edit or delete locations in the admin interface
Browse files Browse the repository at this point in the history
  • Loading branch information
wachterjohannes authored and niklasnatter committed Oct 17, 2022
1 parent 270bb52 commit 6f90bb1
Show file tree
Hide file tree
Showing 9 changed files with 460 additions and 3 deletions.
53 changes: 53 additions & 0 deletions config/forms/location_details.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?xml version="1.0" ?>
<form xmlns="http://schemas.sulu.io/template/template"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.sulu.io/template/form-1.0.xsd"
>
<key>location_details</key>

<properties>
<property name="name" type="text_line" mandatory="true">
<meta>
<title>sulu_admin.name</title>
</meta>
</property>

<property name="street" type="text_line" colspan="9">
<meta>
<title>sulu_contact.street</title>
</meta>
</property>

<property name="number" type="text_line" colspan="3">
<meta>
<title>sulu_contact.number</title>
</meta>
</property>

<property name="postalCode" type="text_line" colspan="3">
<meta>
<title>sulu_contact.zip</title>
</meta>
</property>

<property name="city" type="text_line" colspan="6">
<meta>
<title>sulu_contact.city</title>
</meta>
</property>

<property name="countryCode" type="single_select" colspan="3">
<meta>
<title>sulu_contact.country</title>
</meta>

<params>
<param
name="values"
type="expression"
value="service('App\\Service\\CountryCodeSelect').getValues()"
/>
</params>
</property>
</properties>
</form>
1 change: 1 addition & 0 deletions config/packages/sulu_admin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ sulu_admin:
locations:
routes:
list: app.get_location_list
detail: app.get_location

# Registering Selection Field Types in this section
field_type_options:
Expand Down
3 changes: 3 additions & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ services:
tags: [{name: 'sulu.smart_content.data_provider', alias: 'events'}]
bind:
$repository: '@App\Repository\EventRepository'

App\Service\CountryCodeSelect:
public: true
45 changes: 44 additions & 1 deletion src/Admin/LocationAdmin.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Sulu\Bundle\AdminBundle\Admin\Admin;
use Sulu\Bundle\AdminBundle\Admin\Navigation\NavigationItem;
use Sulu\Bundle\AdminBundle\Admin\Navigation\NavigationItemCollection;
use Sulu\Bundle\AdminBundle\Admin\View\ToolbarAction;
use Sulu\Bundle\AdminBundle\Admin\View\ViewBuilderFactoryInterface;
use Sulu\Bundle\AdminBundle\Admin\View\ViewCollection;

Expand All @@ -17,6 +18,10 @@ class LocationAdmin extends Admin

final public const LOCATION_LIST_VIEW = 'app.locations_list';

final public const LOCATION_ADD_FORM_VIEW = 'app.location_add_form';

final public const LOCATION_EDIT_FORM_VIEW = 'app.location_edit_form';

public function __construct(
private readonly ViewBuilderFactoryInterface $viewBuilderFactory,
) {
Expand All @@ -35,12 +40,50 @@ public function configureNavigationItems(NavigationItemCollection $navigationIte

public function configureViews(ViewCollection $viewCollection): void
{
$listToolbarActions = [
new ToolbarAction('sulu_admin.add'),
new ToolbarAction('sulu_admin.delete'),
];
$listView = $this->viewBuilderFactory->createListViewBuilder(self::LOCATION_LIST_VIEW, '/locations')
->setResourceKey(Location::RESOURCE_KEY)
->setListKey(self::LOCATION_LIST_KEY)
->setTitle('app.locations')
->addListAdapters(['table'])
->addToolbarActions([]);
->setAddView(static::LOCATION_ADD_FORM_VIEW)
->setEditView(static::LOCATION_EDIT_FORM_VIEW)
->addToolbarActions($listToolbarActions);
$viewCollection->add($listView);

$addFormView = $this->viewBuilderFactory->createResourceTabViewBuilder(self::LOCATION_ADD_FORM_VIEW, '/locations/add')
->setResourceKey('locations')
->setBackView(static::LOCATION_LIST_VIEW);
$viewCollection->add($addFormView);

$addDetailsFormView = $this->viewBuilderFactory->createFormViewBuilder(self::LOCATION_ADD_FORM_VIEW . '.details', '/details')
->setResourceKey('locations')
->setFormKey('location_details')
->setTabTitle('sulu_admin.details')
->setEditView(static::LOCATION_EDIT_FORM_VIEW)
->addToolbarActions([new ToolbarAction('sulu_admin.save')])
->setParent(static::LOCATION_ADD_FORM_VIEW);
$viewCollection->add($addDetailsFormView);

$editFormView = $this->viewBuilderFactory->createResourceTabViewBuilder(static::LOCATION_EDIT_FORM_VIEW, '/locations/:id')
->setResourceKey('locations')
->setBackView(static::LOCATION_LIST_VIEW)
->setTitleProperty('title');
$viewCollection->add($editFormView);

$formToolbarActions = [
new ToolbarAction('sulu_admin.save'),
new ToolbarAction('sulu_admin.delete'),
];
$editDetailsFormView = $this->viewBuilderFactory->createFormViewBuilder(static::LOCATION_EDIT_FORM_VIEW . '.details', '/details')
->setResourceKey('locations')
->setFormKey('location_details')
->setTabTitle('sulu_admin.details')
->addToolbarActions($formToolbarActions)
->setParent(static::LOCATION_EDIT_FORM_VIEW);
$viewCollection->add($editDetailsFormView);
}
}
112 changes: 112 additions & 0 deletions src/Controller/Admin/LocationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,80 @@

use App\Common\DoctrineListRepresentationFactory;
use App\Entity\Location;
use App\Repository\LocationRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;

/**
* @phpstan-type LocationData array{
* id: int|null,
* name: string,
* street: string|null,
* number: string|null,
* postalCode: string|null,
* city: string|null,
* countryCode: string|null,
* }
*/
class LocationController extends AbstractController
{
public function __construct(
private readonly LocationRepository $locationRepository,
private readonly DoctrineListRepresentationFactory $doctrineListRepresentationFactory,
) {
}

#[Route(path: '/admin/api/locations/{id}', methods: ['GET'], name: 'app.get_location')]
public function getAction(int $id): Response
{
$location = $this->load($id);
if (!$location instanceof Location) {
throw new NotFoundHttpException();
}

return $this->json($this->getDataForEntity($location));
}

#[Route(path: '/admin/api/locations/{id}', methods: ['PUT'], name: 'app.put_location')]
public function putAction(int $id, Request $request): Response
{
$location = $this->load($id);
if (!$location instanceof Location) {
throw new NotFoundHttpException();
}

/** @var LocationData $data */
$data = $request->toArray();
$this->mapDataToEntity($data, $location);
$this->save($location);

return $this->json($this->getDataForEntity($location));
}

#[Route(path: '/admin/api/locations', methods: ['POST'], name: 'app.post_location')]
public function postAction(Request $request): Response
{
$location = $this->create();

/** @var LocationData $data */
$data = $request->toArray();
$this->mapDataToEntity($data, $location);
$this->save($location);

return $this->json($this->getDataForEntity($location), 201);
}

#[Route(path: '/admin/api/locations/{id}', methods: ['DELETE'], name: 'app.delete_location')]
public function deleteAction(int $id): Response
{
$this->remove($id);

return $this->json(null, 204);
}

#[Route(path: '/admin/api/locations', methods: ['GET'], name: 'app.get_location_list')]
public function getListAction(): Response
{
Expand All @@ -26,4 +89,53 @@ public function getListAction(): Response

return $this->json($listRepresentation->toArray());
}

/**
* @return LocationData
*/
protected function getDataForEntity(Location $entity): array
{
return [
'id' => $entity->getId(),
'name' => $entity->getName() ?? '',
'street' => $entity->getStreet(),
'number' => $entity->getNumber(),
'postalCode' => $entity->getPostalCode(),
'city' => $entity->getCity(),
'countryCode' => $entity->getCountryCode(),
];
}

/**
* @param LocationData $data
*/
protected function mapDataToEntity(array $data, Location $entity): void
{
$entity->setName($data['name']);
$entity->setStreet($data['street'] ?? '');
$entity->setNumber($data['number'] ?? '');
$entity->setPostalCode($data['postalCode'] ?? '');
$entity->setCity($data['city'] ?? '');
$entity->setCountryCode($data['countryCode'] ?? '');
}

protected function load(int $id): ?Location
{
return $this->locationRepository->findById($id);
}

protected function create(): Location
{
return $this->locationRepository->create();
}

protected function save(Location $entity): void
{
$this->locationRepository->save($entity);
}

protected function remove(int $id): void
{
$this->locationRepository->remove($id);
}
}
23 changes: 21 additions & 2 deletions src/Repository/LocationRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,29 @@ public function __construct(ManagerRegistry $registry)

public function create(): Location
{
$location = new Location();
return new Location();
}

public function remove(int $id): void
{
/** @var object $location */
$location = $this->getEntityManager()->getReference(
$this->getClassName(),
$id,
);

$this->getEntityManager()->remove($location);
$this->getEntityManager()->flush();
}

public function save(Location $location): void
{
$this->getEntityManager()->persist($location);
$this->getEntityManager()->flush();
}

return $location;
public function findById(int $id): ?Location
{
return $this->find($id);
}
}
27 changes: 27 additions & 0 deletions src/Service/CountryCodeSelect.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace App\Service;

use Symfony\Component\Intl\Countries;

class CountryCodeSelect
{
/**
* @return mixed[]
*/
public function getValues(): array
{
$values = [];

foreach (Countries::getNames() as $code => $title) {
$values[] = [
'name' => $code,
'title' => $title,
];
}

return $values;
}
}
Loading

0 comments on commit 6f90bb1

Please sign in to comment.