Added endpoints for attachments

This commit is contained in:
Jan Böhmer 2023-09-18 21:57:17 +02:00
parent 8182e83846
commit 077beb37b1
2 changed files with 38 additions and 2 deletions

View file

@ -22,7 +22,13 @@ declare(strict_types=1);
namespace App\Entity\Attachments; namespace App\Entity\Attachments;
use App\Entity\Parts\PartTraits\ProjectTrait; use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use App\Repository\AttachmentRepository; use App\Repository\AttachmentRepository;
use App\EntityListeners\AttachmentDeleteListener; use App\EntityListeners\AttachmentDeleteListener;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
@ -30,6 +36,7 @@ use App\Entity\Base\AbstractNamedDBElement;
use App\Validator\Constraints\Selectable; use App\Validator\Constraints\Selectable;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Annotation\SerializedName;
use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Constraints as Assert;
use function in_array; use function in_array;
use InvalidArgumentException; use InvalidArgumentException;
@ -55,6 +62,18 @@ use LogicException;
#[ORM\Index(name: 'attachments_idx_class_name_id', columns: ['class_name', 'id'])] #[ORM\Index(name: 'attachments_idx_class_name_id', columns: ['class_name', 'id'])]
#[ORM\Index(name: 'attachment_name_idx', columns: ['name'])] #[ORM\Index(name: 'attachment_name_idx', columns: ['name'])]
#[ORM\Index(name: 'attachment_element_idx', columns: ['class_name', 'element_id'])] #[ORM\Index(name: 'attachment_element_idx', columns: ['class_name', 'element_id'])]
#[ApiResource(
operations: [
new Get(security: 'is_granted("read", object)'),
new GetCollection(security: 'is_granted("@attachments.list_attachments")'),
new Post(securityPostDenormalize: 'is_granted("create", object)'),
new Patch(security: 'is_granted("edit", object)'),
new Delete(security: 'is_granted("delete", object)'),
],
normalizationContext: ['groups' => ['attachment:read', 'attachment:read:standalone', 'api:basic:read'], 'openapi_definition_name' => 'Read'],
denormalizationContext: ['groups' => ['attachment:write', 'api:basic:write'], 'openapi_definition_name' => 'Write'],
)]
abstract class Attachment extends AbstractNamedDBElement abstract class Attachment extends AbstractNamedDBElement
{ {
/** /**
@ -102,22 +121,25 @@ abstract class Attachment extends AbstractNamedDBElement
* @var string the name of this element * @var string the name of this element
*/ */
#[Assert\NotBlank(message: 'validator.attachment.name_not_blank')] #[Assert\NotBlank(message: 'validator.attachment.name_not_blank')]
#[Groups(['simple', 'extended', 'full'])] #[Groups(['simple', 'extended', 'full', 'attachment:read', 'attachment:write'])]
protected string $name = ''; protected string $name = '';
/** /**
* ORM mapping is done in subclasses (like PartAttachment). * ORM mapping is done in subclasses (like PartAttachment).
* @phpstan-param T|null $element * @phpstan-param T|null $element
*/ */
#[Groups(['attachment:read:standalone', 'attachment:read'])]
protected ?AttachmentContainingDBElement $element = null; protected ?AttachmentContainingDBElement $element = null;
#[ORM\Column(type: Types::BOOLEAN)] #[ORM\Column(type: Types::BOOLEAN)]
#[Groups(['attachment:read', 'attachment_write'])]
protected bool $show_in_table = false; protected bool $show_in_table = false;
#[Assert\NotNull(message: 'validator.attachment.must_not_be_null')] #[Assert\NotNull(message: 'validator.attachment.must_not_be_null')]
#[ORM\ManyToOne(targetEntity: AttachmentType::class, inversedBy: 'attachments_with_type')] #[ORM\ManyToOne(targetEntity: AttachmentType::class, inversedBy: 'attachments_with_type')]
#[ORM\JoinColumn(name: 'type_id', nullable: false)] #[ORM\JoinColumn(name: 'type_id', nullable: false)]
#[Selectable()] #[Selectable()]
#[Groups(['attachment:read', 'attachment_write'])]
protected ?AttachmentType $attachment_type = null; protected ?AttachmentType $attachment_type = null;
public function __construct() public function __construct()
@ -147,6 +169,7 @@ abstract class Attachment extends AbstractNamedDBElement
* @return bool * true if the file extension is a picture extension * @return bool * true if the file extension is a picture extension
* * otherwise false * * otherwise false
*/ */
#[Groups(['attachment:read'])]
public function isPicture(): bool public function isPicture(): bool
{ {
if ($this->isExternal()) { if ($this->isExternal()) {
@ -171,6 +194,8 @@ abstract class Attachment extends AbstractNamedDBElement
* Check if this attachment is a 3D model and therefore can be directly shown to user. * Check if this attachment is a 3D model and therefore can be directly shown to user.
* If the attachment is external, false is returned (3D Models must be internal). * If the attachment is external, false is returned (3D Models must be internal).
*/ */
#[Groups(['attachment:read'])]
#[SerializedName('3d_model')]
public function is3DModel(): bool public function is3DModel(): bool
{ {
//We just assume that 3D Models are internally saved, otherwise we get problems loading them. //We just assume that 3D Models are internally saved, otherwise we get problems loading them.
@ -188,6 +213,7 @@ abstract class Attachment extends AbstractNamedDBElement
* *
* @return bool true, if the file is saved externally * @return bool true, if the file is saved externally
*/ */
#[Groups(['attachment:read'])]
public function isExternal(): bool public function isExternal(): bool
{ {
//When path is empty, this attachment can not be external //When path is empty, this attachment can not be external
@ -207,6 +233,8 @@ abstract class Attachment extends AbstractNamedDBElement
* *
* @return bool true, if the file is secure * @return bool true, if the file is secure
*/ */
#[Groups(['attachment:read'])]
#[SerializedName('private')]
public function isSecure(): bool public function isSecure(): bool
{ {
//After the %PLACEHOLDER% comes a slash, so we can check if we have a placeholder via explode //After the %PLACEHOLDER% comes a slash, so we can check if we have a placeholder via explode
@ -221,6 +249,7 @@ abstract class Attachment extends AbstractNamedDBElement
* *
* @return bool true if the attachment is using a builtin file * @return bool true if the attachment is using a builtin file
*/ */
#[Groups(['attachment:read'])]
public function isBuiltIn(): bool public function isBuiltIn(): bool
{ {
return static::checkIfBuiltin($this->path); return static::checkIfBuiltin($this->path);
@ -267,6 +296,8 @@ abstract class Attachment extends AbstractNamedDBElement
* The URL to the external file, or the path to the built-in file. * The URL to the external file, or the path to the built-in file.
* Returns null, if the file is not external (and not builtin). * Returns null, if the file is not external (and not builtin).
*/ */
#[Groups(['attachment:read'])]
#[SerializedName('url')]
public function getURL(): ?string public function getURL(): ?string
{ {
if (!$this->isExternal() && !$this->isBuiltIn()) { if (!$this->isExternal() && !$this->isBuiltIn()) {
@ -417,6 +448,8 @@ abstract class Attachment extends AbstractNamedDBElement
* *
* @return Attachment * @return Attachment
*/ */
#[Groups(['attachment:write'])]
#[SerializedName('url')]
public function setURL(?string $url): self public function setURL(?string $url): self
{ {
//Only set if the URL is not empty //Only set if the URL is not empty

View file

@ -42,6 +42,7 @@ use DateTime;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Annotation\SerializedName;
use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Constraints as Assert;
/** /**
@ -189,6 +190,7 @@ class Pricedetail extends AbstractDBElement implements TimeStampableInterface
* @return BigDecimal the price as a bcmath string * @return BigDecimal the price as a bcmath string
*/ */
#[Groups(['pricedetail:read'])] #[Groups(['pricedetail:read'])]
#[SerializedName('price_per_unit')]
public function getPricePerUnit(float|string|BigDecimal $multiplier = 1.0): BigDecimal public function getPricePerUnit(float|string|BigDecimal $multiplier = 1.0): BigDecimal
{ {
$tmp = BigDecimal::of($multiplier); $tmp = BigDecimal::of($multiplier);
@ -256,6 +258,7 @@ class Pricedetail extends AbstractDBElement implements TimeStampableInterface
* @return string|null * @return string|null
*/ */
#[Groups(['pricedetail:read'])] #[Groups(['pricedetail:read'])]
#[SerializedName('currency_iso_code')]
public function getCurrencyISOCode(): ?string public function getCurrencyISOCode(): ?string
{ {
return $this->currency?->getIsoCode(); return $this->currency?->getIsoCode();