<?php
declare(strict_types=1);
namespace App\Entity\Product;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Entity\Trait\ReferenceableTrait;
use Dedi\SyliusSEOPlugin\Domain\SEO\Adapter\ReferenceableInterface;
use Dedi\SyliusSEOPlugin\Domain\SEO\Adapter\RichSnippetProductSubjectInterface;
use Dedi\SyliusSEOPlugin\Domain\SEO\Adapter\RichSnippetProductSubjectTrait;
use Dedi\SyliusSEOPlugin\Domain\SEO\Adapter\RichSnippetSubjectInterface;
use Dedi\SyliusSEOPlugin\Entity\SEOContent;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Loevgaard\SyliusBrandPlugin\Model\BrandAwareInterface;
use Loevgaard\SyliusBrandPlugin\Model\ProductTrait as LoevgaardSyliusBrandPluginProductTrait;
use Mezcalito\SyliusFileUploadPlugin\Model\FileInterface;
use Mezcalito\SyliusFileUploadPlugin\Model\FilesAwareInterface;
use Mezcalito\SyliusFileUploadPlugin\Model\FilesAwareTrait;
use Setono\SyliusCalloutPlugin\Model\CalloutsAwareInterface;
use Setono\SyliusCalloutPlugin\Model\CalloutsAwareTrait as SetonoSyliusCalloutCalloutsAwareTrait;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\CustomerInterface;
use Sylius\Component\Core\Model\Product as BaseProduct;
use Sylius\Component\Product\Model\ProductTranslationInterface;
use Sylius\Component\Product\Model\ProductVariantInterface;
use Tavy315\SyliusLabelsPlugin\Model\LabelsAwareInterface;
use Tavy315\SyliusLabelsPlugin\Model\LabelsAwareTrait;
/**
* @ORM\Entity
* @ORM\Table(name="sylius_product")
* @ApiResource()
*/
class Product extends BaseProduct implements
ProductInterface,
ReferenceableInterface,
RichSnippetProductSubjectInterface,
BrandAwareInterface,
CalloutsAwareInterface,
LabelsAwareInterface,
FilesAwareInterface
{
use ReferenceableTrait {
getMetadataTitle as getBaseMetadataTitle;
getMetadataDescription as getBaseMetadataDescription;
getOpenGraphMetadataImage as getBaseOpenGraphMetadataImage;
}
use RichSnippetProductSubjectTrait;
use LoevgaardSyliusBrandPluginProductTrait;
use LabelsAwareTrait {
LabelsAwareTrait::__construct as private __labelsTraitConstruct;
}
use SetonoSyliusCalloutCalloutsAwareTrait {
SetonoSyliusCalloutCalloutsAwareTrait::__construct as private __calloutsTraitConstruct;
}
use FilesAwareTrait {
FilesAwareTrait::__construct as private initializeFilesCollection;
}
/**
* @ORM\Column(type="string", length=50, unique=true, nullable=true)
*/
private $ean;
/**
* @ORM\Column(type="integer", nullable=true)
*/
private $minQuantity = 0;
/**
* @ORM\Column(type="integer", nullable=true)
*/
private $minPrice = 0;
/**
* @ORM\Column(type="integer", nullable=true)
*/
private $maxPrice = 0;
/**
* @ORM\Column(type="integer", nullable=true)
*/
private $markup = 0;
/**
* @ORM\Column(type="boolean", nullable=false)
*/
private $onlyBreakpoints = false;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Product\ProductFileInterface", mappedBy="owner", orphanRemoval=true, cascade={"all"})
* @var Collection|FileInterface[]
*/
protected $files;
/**
* @ORM\OneToMany(targetEntity="ProductVariant", mappedBy="product", orphanRemoval=true, cascade={"persist", "remove"})
* @var Collection|ProductVariantInterface[]
*/
protected $variants;
public function __construct()
{
$this->__labelsTraitConstruct();
$this->__calloutsTraitConstruct();
$this->initializeFilesCollection();
parent::__construct();
$this->files = new ArrayCollection();
}
public function getMetadataTitle(): ?string
{
if (null === $this->getReferenceableContent()->getMetadataTitle()) {
$this->setCurrentLocale($this->getReferenceableContent()->getTranslation()->getLocale());
return null === $this->getMainTaxon() ? $this->getName() :
$this->getMainTaxon()->getName() . ' | ' . $this->getName();
}
return $this->getBaseMetadataTitle();
}
public function getMetadataDescription(): ?string
{
if (null === $this->getReferenceableContent()->getMetadataDescription()) {
$this->setCurrentLocale($this->getReferenceableContent()->getTranslation()->getLocale());
return $this->getShortDescription();
}
return $this->getBaseMetadataDescription();
}
public function getOpenGraphMetadataImage(): ?string
{
if (
null === $this->getReferenceableContent()->getOpenGraphMetadataImage() && $this->getImages() && $this->getImages()->first()
) {
return $this->getImages()->first()->getPath();
}
return $this->getBaseOpenGraphMetadataImage();
}
protected function createReferenceableContent(): ReferenceableInterface
{
return new SEOContent();
}
public function getRichSnippetSubjectParent(): ?RichSnippetSubjectInterface
{
return $this->getMainTaxon();
}
public function getRichSnippetSubjectType(): string
{
return 'product';
}
public function getEan(): ?string
{
return $this->ean;
}
public function setEan(string $ean = null): void
{
$this->ean = $ean;
}
public function getMinQuantity(): ?int
{
return $this->minQuantity;
}
public function setMinQuantity(int $minQuantity = null): void
{
$this->minQuantity = $minQuantity;
}
public function getMinPrice(): ?int
{
return $this->minPrice;
}
public function setMinPrice(int $minPrice = null): void
{
$this->minPrice = $minPrice;
}
public function getMaxPrice(): ?int
{
return $this->maxPrice;
}
public function setMaxPrice(int $maxPrice = null): void
{
$this->maxPrice = $maxPrice;
}
public function getMarkup(): ?int
{
return $this->markup;
}
public function setMarkup(int $markup = null): void
{
$this->markup = $markup;
}
public function isOnlyBreakpoints(): bool
{
return $this->onlyBreakpoints;
}
public function setOnlyBreakpoints(bool $onlyBreakpoints): void
{
$this->onlyBreakpoints = $onlyBreakpoints;
}
protected function createTranslation(): ProductTranslationInterface
{
return new ProductTranslation();
}
public function getFirstVariant()
{
return $this->isSimple() ? $this->variants[0] : null;
}
public function getMainImage()
{
foreach ($this->getImages() as $key => $value) {
if ($value->getType() === 'main') {
return $value;
}
}
return null;
}
public function getVariantsBreakpoints(ChannelInterface $channel, ?CustomerInterface $customer = null): array
{
$out = [];
foreach ($this->getEnabledVariants() as $productVariant) {
$tierPrices = $productVariant->getTierPricesForChannel($channel, $customer = null);
$code = $productVariant->getCode();
$ovs = $productVariant->getOptionValues();
if (count($ovs) === 1) {
foreach ($tierPrices as $tierPrice) {
$qty = $tierPrice->getQty();
// vyhodime nulove(N/A) breakpointy
if ($tierPrice->getPrice()) {
$out[$ovs[0]->getCode()][$qty] = $qty;
$out['all'][$qty] = $qty;
}
}
}
}
if (!empty($out['all']) && is_array($out['all'])) {
ksort($out['all']);
}
return $out;
}
public function variantsBreakpointsPricesForJs(ChannelInterface $channel, ?CustomerInterface $customer = null): array
{
$out = [];
foreach ($this->getEnabledVariants() as $productVariant) {
$tierPrices = $productVariant->getTierPricesForChannel($channel, $customer = null);
foreach ($tierPrices as $tierPrice) {
$qty = $tierPrice->getQty();
$code = $productVariant->getCode();
$options = [];
foreach ($productVariant->getOptionValues() as $ov) {
// vsetky product options pre variants; ak zvolime vsetky x kombinacie options, potom je to tento variant
$options[$ov->getOption()->getCode()] = [
'id' => $ov->getId(),
'code' => $ov->getCode()
];
}
$out[$code][$qty] = [
'variant' => $code,
'qty' => $qty,
'price' => $tierPrice->getPrice(),
'options' => $options
];
if (is_array($out[$code])) {
ksort($out[$code]);
}
}
}
return $out;
}
public function getEnabledVariantsWith1Option(): Collection
{
return $this->variants->filter(
function (ProductVariantInterface $productVariant) {
return $productVariant->isEnabled() && count($productVariant->getOptionValues()) === 1 && $productVariant->getOptionValues()->first()->getOption()->getCode() === 'type';
}
);
}
public function calculateMinQty(): ?int
{
$min = null;
foreach ($this->getEnabledVariants() as $productVariant) {
$tierPrices = $productVariant->getTierPrices();
foreach ($tierPrices as $tierPrice) {
if ($tierPrice->getQty() < $min || $min === null) {
$min = $tierPrice->getQty();
}
}
}
return $min ?: 0;
}
public function getEnabledVariants(): Collection
{
$variants = $this->variants->filter(
function (ProductVariantInterface $productVariant) {
return $productVariant->isEnabled();
},
);
$variants = [...$variants];
foreach($variants as $key => $variant) {
if (count($variant->getOptionValues()) === 1 && $variant->getOptionValues()[0]->getCode() === 'stock-plain') {
unset($variants[$key]);
$variants = array_merge([$variant], $variants);
break;
}
}
return new ArrayCollection($variants);
}
}