Added an export function to attachment_types admin pages.

This commit is contained in:
Jan Böhmer 2019-04-07 19:30:42 +02:00
parent 09eb3c226a
commit 091311cdf1
6 changed files with 199 additions and 20 deletions

View file

@ -33,11 +33,16 @@ namespace App\Controller;
use App\Entity\AttachmentType; use App\Entity\AttachmentType;
use App\Entity\StructuralDBElement;
use App\Form\BaseEntityAdminForm; use App\Form\BaseEntityAdminForm;
use App\Form\ExportType;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\SerializerInterface;
/** /**
* @Route("/attachment_type") * @Route("/attachment_type")
@ -119,4 +124,91 @@ class AttachmentTypeController extends AbstractController
return $this->redirectToRoute('attachment_type_new'); return $this->redirectToRoute('attachment_type_new');
} }
/**
* @Route("/export")
* @param Request $request
* @param SerializerInterface $serializer
* @param EntityManagerInterface $em
* @return Response
*/
public function exportAll(Request $request, SerializerInterface $serializer, EntityManagerInterface $em)
{
$entities = $em->getRepository(AttachmentType::class)->findAll();
return $this->exportHelper($entities, $request, $serializer);
}
/**
* @Route("/{id}/export", name="attachment_type_export")
* @param Request $request
* @param AttachmentType $entity
*/
public function exportEntity(Request $request, AttachmentType $entity, SerializerInterface $serializer)
{
return $this->exportHelper($entity, $request, $serializer);
}
protected function exportHelper($entity, Request $request, SerializerInterface $serializer) : Response
{
$format = $request->get('format') ?? "json";
//Check if we have one of the supported formats
if (!in_array($format, ['json', 'csv', 'yaml', 'xml'])) {
throw new \InvalidArgumentException("Given format is not supported!");
}
//Check export verbosity level
$level = $request->get('level') ?? 'extended';
if (!in_array($level, ['simple', 'extended', 'full'])) {
throw new \InvalidArgumentException('Given level is not supported!');
}
//Check for include children option
$include_children = $request->get('include_children') ?? false;
//Check which groups we need to export, based on level and include_children
$groups = array($level);
if ($include_children) {
$groups[] = 'include_children';
}
//Plain text should work for all types
$content_type = "text/plain";
//Try to use better content types based on the format
switch ($format) {
case 'xml':
$content_type = "application/xml";
break;
case 'json':
$content_type = "application/json";
break;
}
$response = new Response($serializer->serialize($entity, $format,
[
'groups' => $groups,
'as_collection' => true,
'csv_delimiter' => ';', //Better for Excel
'xml_root_node_name' => 'PartDBExport'
]));
$response->headers->set('Content-Type', $content_type);
//If view option is not specified, then download the file.
if (!$request->get('view')) {
$filename = "export_" . $entity->getName() . "_" . $level . "." . $format;
// Create the disposition of the file
$disposition = $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
$filename
);
// Set the content disposition
$response->headers->set('Content-Disposition', $disposition);
}
return $response;
}
} }

View file

@ -77,7 +77,6 @@ class AttachmentType extends StructuralDBElement
*/ */
public function getIDString(): string public function getIDString(): string
{ {
return ''; return 'AT' . sprintf('%09d', $this->getID());
//return 'AT' . sprintf('%09d', $this->getID());
} }
} }

View file

@ -24,6 +24,8 @@ declare(strict_types=1);
namespace App\Entity; namespace App\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
use Symfony\Component\Serializer\Annotation\Groups;
/** /**
* This class is for managing all database objects. * This class is for managing all database objects.
@ -36,6 +38,23 @@ use Doctrine\ORM\Mapping as ORM;
* @ORM\MappedSuperclass() * @ORM\MappedSuperclass()
* *
* @ORM\EntityListeners({"App\Security\EntityListeners\ElementPermissionListener"}) * @ORM\EntityListeners({"App\Security\EntityListeners\ElementPermissionListener"})
*
* @DiscriminatorMap(typeProperty="type", mapping={
* "attachment_type" = "App\Entity\AttachmentType",
* "attachment" = "App\Entity\Attachment",
* "category" = "App\Entity\Attachment",
* "device" = "App\Entity\Device",
* "device_part" = "App\Entity\DevicePart",
* "footprint" = "App\Entity\Footprint",
* "group" = "App\Entity\Group",
* "manufacturer" = "App\Entity\Manufacturer",
* "orderdetail" = "App\Entity\Orderdetail",
* "part" = "App\Entity\Part",
* "pricedetail" = "App\Entity\Pricedetail",
* "storelocation" = "App\Entity\Storelocation",
* "supplier" = "App\Entity\Supplier",
* "user" = "App\Entity\User"
* })
*/ */
abstract class DBElement abstract class DBElement
{ {
@ -43,6 +62,7 @@ abstract class DBElement
* @ORM\Column(type="integer") * @ORM\Column(type="integer")
* @ORM\Id() * @ORM\Id()
* @ORM\GeneratedValue() * @ORM\GeneratedValue()
* @Groups({"full"})
*/ */
protected $id; protected $id;
@ -64,6 +84,7 @@ abstract class DBElement
* This should have a form like P000014, for a part with ID 14. * This should have a form like P000014, for a part with ID 14.
* *
* @return string The ID as a string; * @return string The ID as a string;
*
*/ */
abstract public function getIDString(): string; abstract public function getIDString(): string;

View file

@ -25,6 +25,7 @@ namespace App\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Serializer\Annotation\Groups;
/** /**
* All subclasses of this class have an attribute "name". * All subclasses of this class have an attribute "name".
@ -38,18 +39,21 @@ abstract class NamedDBElement extends DBElement
* @var string The name of this element. * @var string The name of this element.
* @ORM\Column(type="string") * @ORM\Column(type="string")
* @Assert\NotBlank() * @Assert\NotBlank()
* @Groups({"simple", "extended", "full"})
*/ */
protected $name = ''; protected $name = '';
/** /**
* @var \DateTime The date when this element was modified the last time. * @var \DateTime The date when this element was modified the last time.
* @ORM\Column(type="datetimetz", name="last_modified") * @ORM\Column(type="datetimetz", name="last_modified")
* @Groups({"extended", "full"})
*/ */
protected $lastModified; protected $lastModified;
/** /**
* @var \DateTime The date when this element was created. * @var \DateTime The date when this element was created.
* @ORM\Column(type="datetimetz", name="datetime_added") * @ORM\Column(type="datetimetz", name="datetime_added")
* @Groups({"extended", "full"})
*/ */
protected $addedDate; protected $addedDate;

View file

@ -27,6 +27,8 @@ use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\PersistentCollection;
use App\Validator\Constraints\NoneOfItsChildren; use App\Validator\Constraints\NoneOfItsChildren;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
use Symfony\Component\Serializer\Annotation\Groups;
/** /**
* All elements with the fields "id", "name" and "parent_id" (at least). * All elements with the fields "id", "name" and "parent_id" (at least).
@ -52,17 +54,20 @@ abstract class StructuralDBElement extends AttachmentContainingDBElement
// subclasses // subclasses
/** /**
* @var StructuralDBElement[] * @var StructuralDBElement[]
* @Groups({"include_children"})
*/ */
protected $children; protected $children;
/** /**
* @var StructuralDBElement * @var StructuralDBElement
* @NoneOfItsChildren() * @NoneOfItsChildren()
* @Groups({"include_parents"})
*/ */
protected $parent; protected $parent;
/** /**
* @var string The comment info for this element * @var string The comment info for this element
* @ORM\Column(type="string", nullable=true) * @ORM\Column(type="string", nullable=true)
* @Groups({"simple", "extended", "full"})
*/ */
protected $comment; protected $comment;
@ -78,7 +83,9 @@ abstract class StructuralDBElement extends AttachmentContainingDBElement
protected $level = 0; protected $level = 0;
/** @var string[] all names of all parent elements as a array of strings, /** @var string[] all names of all parent elements as a array of strings,
* the last array element is the name of the element itself */ * the last array element is the name of the element itself
*
*/
private $full_path_strings; private $full_path_strings;
/****************************************************************************** /******************************************************************************
@ -154,24 +161,25 @@ abstract class StructuralDBElement extends AttachmentContainingDBElement
/** /**
* Get the level. * Get the level.
* *
* The level of the root node is -1. * The level of the root node is -1.
* *
* @return int the level of this element (zero means a most top element * @return int the level of this element (zero means a most top element
* [a subelement of the root node]) * [a subelement of the root node])
*
*/ */
public function getLevel(): int public function getLevel(): int
{ {
if (0 === $this->level) { /**
* Only check for nodes that have a parent. In the other cases zero is correct.
*/
if (0 === $this->level && $this->parent !== null) {
$element = $this->parent; $element = $this->parent;
$parent_id = $element->getParentID(); while ($element !== null) {
while ($parent_id > 0) {
/** @var StructuralDBElement $element */ /** @var StructuralDBElement $element */
$element = $element->parent; $element = $element->parent;
$parent_id = $element->getParentID();
++$this->level; ++$this->level;
} }
} }
return $this->level; return $this->level;
} }
@ -181,6 +189,7 @@ abstract class StructuralDBElement extends AttachmentContainingDBElement
* @param string $delimeter the delimeter of the returned string * @param string $delimeter the delimeter of the returned string
* *
* @return string the full path (incl. the name of this element), delimeted by $delimeter * @return string the full path (incl. the name of this element), delimeted by $delimeter
*
*/ */
public function getFullPath(string $delimeter = self::PATH_DELIMITER_ARROW): string public function getFullPath(string $delimeter = self::PATH_DELIMITER_ARROW): string
{ {
@ -216,6 +225,11 @@ abstract class StructuralDBElement extends AttachmentContainingDBElement
return $this->children; return $this->children;
} }
public function getChildren(): PersistentCollection
{
return $this->children;
}
/****************************************************************************** /******************************************************************************
* *
* Setters * Setters

View file

@ -29,8 +29,6 @@
<div class="col-8"> <div class="col-8">
{{ form_start(form) }}
<fieldset> <fieldset>
<legend> <legend>
{% if entity.ID %} {% if entity.ID %}
@ -43,14 +41,26 @@
<ul class="nav nav-tabs mt-2"> <ul class="nav nav-tabs mt-2">
<li class="nav-item"><a class="link-anchor active nav-link" data-toggle="tab" href="#home">standard.label</a></li> <li class="nav-item"><a class="link-anchor active nav-link" data-toggle="tab" href="#home">standard.label</a></li>
<li class="nav-item"><a data-toggle="tab" class="link-anchor nav-link" href="#info">infos.label</a></li> <li class="nav-item"><a data-toggle="tab" class="link-anchor nav-link" href="#info">infos.label</a></li>
{% if entity.id %}
<li class="nav-item"><a data-toggle="tab" class="link-anchor nav-link" href="#export">export.label</a> </li>
{% endif %}
</ul> </ul>
<div class="tab-content mb-3 mt-3"> <div class="tab-content mb-3 mt-3">
<div id="home" class="tab-pane fade show active"> <div id="home" class="tab-pane fade show active">
{{ form_start(form) }}
{{ form_row(form.name) }} {{ form_row(form.name) }}
{{ form_row(form.parent) }} {{ form_row(form.parent) }}
{% block additional_controls %}{% endblock %} {% block additional_controls %}{% endblock %}
{{ form_row(form.comment) }} {{ form_row(form.comment) }}
{{ form_row(form.save) }}
{{ form_row(form.reset) }}
{{ form_end(form) }}
{# Only include on existing parts #}
{% if entity.id %}
{{ include('AdminPages/_delete_form.html.twig') }}
{% endif %}
</div> </div>
<div id="info" class="tab-pane fade"> <div id="info" class="tab-pane fade">
@ -87,20 +97,59 @@
</div> </div>
</div> </div>
</div> </div>
{% if entity.id %}
<div id="export" class="tab-pane fade">
<form class="form-horizontal" data-no-ajax method="post" action="{{ path('attachment_type_export', {'id': entity.id}) }}">
<div class="form-row">
<label class="col-form-label col-md-2">{% trans %}export.format{% endtrans %}</label>
<div class="col">
<select class="form-control" name="format">
<option value="json">JSON</option>
<option value="xml">XML</option>
<option value="csv">CSV</option>
<option value="yaml">YAML</option>
</select>
</div>
</div>
<div class="form-row mt-2">
<label class="col-form-label col-md-2">{% trans %}export.level{% endtrans %}</label>
<div class="col">
<select class="form-control" name="level">
<option value="simple">{% trans %}export.level.simple{% endtrans %}</option>
<option value="extended" selected>{% trans %}export.level.extended{% endtrans %}</option>
<option value="full">{% trans %}export.level.full{% endtrans %}</option>
</select>
</div>
</div>
<div class="form-row mt-2">
<div class="offset-2 col">
<div class="form-check abc-checkbox">
<input class="form-check-input" name="include_children" id="include_children" type="checkbox" checked>
<label class="form-check-label" for="include_children">
{% trans %}export.include_children{% endtrans %}
</label>
</div>
</div>
</div>
<div class="form-row mt-2">
<div class="offset-2 col">
<button type="submit" class="btn btn-primary">{% trans %}export.btn{% endtrans %}</button>
</div>
</div>
</form>
</div>
{% endif %}
</div> </div>
{{ form_row(form.save) }}
{{ form_row(form.reset) }}
</fieldset> </fieldset>
{{ form_end(form) }}
{# Only include on existing parts #}
{% if entity.id %}
{{ include('AdminPages/_delete_form.html.twig') }}
{% endif %}
</div> </div>
</div> </div>