src/Manager/AttributeManager.php line 147

Open in your IDE?
  1. <?php
  2. namespace App\Manager;
  3. use Doctrine\ORM\EntityManagerInterface;
  4. use App\Entity\Product;
  5. use App\Entity\ProductModel;
  6. use App\Entity\Category;
  7. use App\Entity\Attribute;
  8. use App\Entity\Language;
  9. use App\Entity\ProductAttribute;
  10. class AttributeManager {
  11. private $em;
  12. private $projectDir;
  13. private $attributeCache = [];
  14. public function __construct(EntityManagerInterface $em, \Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface $parameter)
  15. {
  16. $this->em = $em;
  17. $this->projectDir = $parameter->get('kernel.project_dir').'/';
  18. }
  19. public function createAttribute(string $code): Attribute{
  20. $attribute = new Attribute();
  21. $attribute->setCode($code);
  22. $attribute->setStatus(0);
  23. $this->em->persist($attribute);
  24. $this->em->flush();
  25. return $attribute;
  26. }
  27. public function getAttribute(string $code): ?Attribute{
  28. if (isset($this->attributeCache[$code])) {
  29. return $this->attributeCache[$code];
  30. }
  31. $attribute = $this->em->getRepository(Attribute::class)->findOneByCode($code);
  32. if (!$attribute) {
  33. $attribute = $this->createAttribute($code);
  34. }
  35. $this->attributeCache[$code] = $attribute;
  36. return $attribute;
  37. }
  38. /**
  39. * The cached Attribute entities become detached after an em->clear(); call this right after
  40. * to avoid reusing stale references on the next lookups.
  41. */
  42. public function resetCache(): void {
  43. $this->attributeCache = [];
  44. }
  45. public function createAttributeDescription(Attribute $attribute, $isoCode = 'fr'): \App\Entity\AttributeDescription{
  46. $language = $this->em->getRepository(\App\Entity\Language::class)->findOneByCode($isoCode);
  47. $ad = new \App\Entity\AttributeDescription();
  48. $ad->setAttribute($attribute);
  49. $ad->setLanguage($language);
  50. $ad->setTitle('');
  51. return $ad;
  52. }
  53. public function createProductAttributeDescription(ProductAttribute $pa, $isoCode = 'fr'): \App\Entity\ProductAttributeDescription{
  54. $language = $this->em->getRepository(\App\Entity\Language::class)->findOneByCode($isoCode);
  55. $ad = new \App\Entity\ProductAttributeDescription();
  56. $ad->setProductAttribute($pa);
  57. $ad->setProductAttribute($pa);
  58. $ad->setLanguage($language);
  59. $ad->setValue('');
  60. return $ad;
  61. }
  62. public function addToProduct(Attribute $attribute, Product $product, string $value, $locale = null, bool $flush = true) {
  63. $pa = $product->getAttribute($attribute->getCode());
  64. if(empty($pa)) {
  65. $pa = new \App\Entity\ProductAttribute();
  66. $pa->setAttribute($attribute);
  67. $pa->setProduct($product);
  68. }
  69. if(is_null($locale)) {
  70. $pa->setValue($value);
  71. }else{
  72. $description = $pa->getDescription($locale);
  73. if(empty($description)) {
  74. $description = $this->createProductAttributeDescription($pa, $locale);
  75. }
  76. $description->setValue($value);
  77. $this->em->persist($description);
  78. }
  79. $this->em->persist($pa);
  80. if ($flush) {
  81. $this->em->flush();
  82. }
  83. }
  84. public function removeFromProduct(Attribute $attribute, Product $product) {
  85. $pas = $this->em->getRepository('App:ProductAttribute')->findBy([
  86. 'product' => $product,
  87. 'attribute' => $attribute
  88. ]);
  89. foreach($pas as $pa){
  90. $this->em->remove($pa);
  91. }
  92. $this->em->flush();
  93. }
  94. public function addToProductModel(Attribute $attribute, ProductModel $product, string $value) {
  95. $pa = $this->em->getRepository('App:ProductModelAttribute')->findOneBy([
  96. 'attribute' => $attribute,
  97. 'productModel' => $product
  98. ]);
  99. if(empty($pa)) {
  100. $pa = new \App\Entity\ProductModelAttribute();
  101. $pa->setAttribute($attribute);
  102. $pa->setProductModel($product);
  103. }
  104. $pa->setValue($value);
  105. $this->em->persist($pa);
  106. $this->em->flush();
  107. }
  108. public function removeFromProductModel(Attribute $attribute, ProductModel $model) {
  109. $pas = $this->em->getRepository('App:ProductModelAttribute')->findBy([
  110. 'productModel' => $model,
  111. 'attribute' => $attribute
  112. ]);
  113. foreach($pas as $pa){
  114. $this->em->remove($pa);
  115. }
  116. $this->em->flush();
  117. }
  118. public function getAttributesByProductModel(ProductModel $productModel): array {
  119. $attributes = [];
  120. $variant = $productModel->getVariant();
  121. if (!$variant) {
  122. return $attributes;
  123. }
  124. foreach ([$variant->getAxis1(), $variant->getAxis2()] as $axis) {
  125. if (!$axis) {
  126. continue;
  127. }
  128. $codes = array_values(array_filter(array_map('trim', explode(',', $axis))));
  129. if (count($codes) === 1) {
  130. $attribute = $this->getAttribute($codes[0]);
  131. if ($attribute && $attribute->isVariant()) {
  132. $attribute->setValues($this->getAttributeValuesByProductModel($attribute, $productModel));
  133. $attributes[] = $attribute;
  134. }
  135. } else {
  136. $composite = $this->buildCompositeAxis($codes, $productModel);
  137. if ($composite) {
  138. $attributes[] = $composite;
  139. }
  140. }
  141. }
  142. return $attributes;
  143. }
  144. /**
  145. * Builds a synthetic axis object for multi-attribute axes (e.g. axis = "taille,couleur").
  146. * Returns a stdClass with code/title/display/values so Twig can render it like a regular Attribute.
  147. * Each value has ->value (combined label "XS / Rouge") and ->product (the matching Product).
  148. */
  149. private function buildCompositeAxis(array $codes, ProductModel $productModel): ?\stdClass {
  150. $axisAttributes = [];
  151. foreach ($codes as $code) {
  152. $attr = $this->em->getRepository(Attribute::class)->findOneByCode($code);
  153. if ($attr) {
  154. $axisAttributes[] = $attr;
  155. }
  156. }
  157. if (empty($axisAttributes)) {
  158. return null;
  159. }
  160. $values = [];
  161. $seen = [];
  162. foreach ($productModel->getAllProducts() as $product) {
  163. $parts = [];
  164. foreach ($axisAttributes as $attr) {
  165. $pa = $product->getAttribute($attr->getCode());
  166. $parts[] = $pa ? ($pa->getDisplayedValue() ?? '?') : '?';
  167. }
  168. $label = implode(' / ', $parts);
  169. if (in_array($label, $seen)) {
  170. continue;
  171. }
  172. $seen[] = $label;
  173. $value = new \stdClass();
  174. $value->value = $label;
  175. $value->product = $product;
  176. $values[] = $value;
  177. }
  178. $composite = new \stdClass();
  179. $composite->code = implode(',', $codes);
  180. $composite->title = implode(' / ', array_map(fn(Attribute $a) => $a->getTitle(), $axisAttributes));
  181. $composite->display = Attribute::DISPLAY_TYPE_COMBOBOX;
  182. $composite->values = $values;
  183. return $composite;
  184. }
  185. public function getAttributeValuesByProductModel(Attribute $attribute, ProductModel $productModel) {
  186. $values = [];
  187. $tmp = [];
  188. $products = $productModel->getAllProducts();
  189. foreach($products as $product) {
  190. $attr = $product->getAttribute($attribute->getCode());
  191. if($attr && !in_array($attr->getValue(), $tmp)) {
  192. $values[] = $attr;
  193. $tmp[] = $attr->getValue();
  194. }
  195. }
  196. return $values;
  197. }
  198. }