classes/XLite/Model/Attribute.php line 686

Open in your IDE?
  1. <?php
  2. /**
  3.  * Copyright (c) 2011-present Qualiteam software Ltd. All rights reserved.
  4.  * See https://www.x-cart.com/license-agreement.html for license details.
  5.  */
  6. namespace XLite\Model;
  7. use ApiPlatform\Core\Annotation as ApiPlatform;
  8. use Doctrine\ORM\Mapping as ORM;
  9. use XLite\API\Endpoint\Attribute\Checkbox\DTO\AttributeCheckboxInput as InputCheckbox;
  10. use XLite\API\Endpoint\Attribute\Checkbox\DTO\AttributeCheckboxOutput as OutputCheckbox;
  11. use XLite\API\Endpoint\Attribute\Hidden\DTO\AttributeHiddenInput as InputHidden;
  12. use XLite\API\Endpoint\Attribute\Hidden\DTO\AttributeHiddenOutput as OutputHidden;
  13. use XLite\API\Endpoint\Attribute\Select\DTO\AttributeSelectInput as InputSelect;
  14. use XLite\API\Endpoint\Attribute\Select\DTO\AttributeSelectOutput as OutputSelect;
  15. use XLite\API\Endpoint\Attribute\Text\DTO\AttributeTextInput as InputText;
  16. use XLite\API\Endpoint\Attribute\Text\DTO\AttributeTextOutput as OutputText;
  17. use XLite\API\Endpoint\ProductAttribute\Checkbox\DTO\ProductAttributeCheckboxInput as ProductAttributeInputCheckbox;
  18. use XLite\API\Endpoint\ProductAttribute\Checkbox\DTO\ProductAttributeCheckboxOutput as ProductAttributeOutputCheckbox;
  19. use XLite\API\Endpoint\ProductAttribute\Select\DTO\ProductAttributeSelectInput as ProductAttributeInputSelect;
  20. use XLite\API\Endpoint\ProductAttribute\Select\DTO\ProductAttributeSelectOutput as ProductAttributeOutputSelect;
  21. use XLite\API\Endpoint\ProductAttribute\Text\DTO\ProductAttributeTextInput as ProductAttributeInputText;
  22. use XLite\API\Endpoint\ProductAttribute\Text\DTO\ProductAttributeTextOutput as ProductAttributeOutputText;
  23. use XLite\Core\Cache\ExecuteCachedTrait;
  24. use XLite\Core\Database;
  25. /**
  26.  * @ORM\Entity
  27.  * @ORM\Table (name="attributes")
  28.  * @ApiPlatform\ApiResource(
  29.  *     itemOperations={
  30.  *          "get_text"={
  31.  *              "method"="GET",
  32.  *              "path"="/attributes_text/{id}.{_format}",
  33.  *              "identifiers"={"id"},
  34.  *              "input"=InputText::class,
  35.  *              "output"=OutputText::class,
  36.  *              "openapi_context"={
  37.  *                  "summary"="Retrieve a global textarea attribute",
  38.  *                  "parameters"={
  39.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  40.  *                  }
  41.  *              }
  42.  *          },
  43.  *          "put_text"={
  44.  *              "method"="PUT",
  45.  *              "path"="/attributes_text/{id}.{_format}",
  46.  *              "identifiers"={"id"},
  47.  *              "input"=InputText::class,
  48.  *              "output"=OutputText::class,
  49.  *              "openapi_context"={
  50.  *                  "summary"="Update a global textarea attribute",
  51.  *                  "parameters"={
  52.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  53.  *                  }
  54.  *              }
  55.  *          },
  56.  *          "delete_text"={
  57.  *              "method"="DELETE",
  58.  *              "path"="/attributes_text/{id}.{_format}",
  59.  *              "identifiers"={"id"},
  60.  *              "input"=InputText::class,
  61.  *              "output"=OutputText::class,
  62.  *              "openapi_context"={
  63.  *                  "summary"="Delete a global textarea attribute",
  64.  *                  "parameters"={
  65.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  66.  *                  }
  67.  *              }
  68.  *          },
  69.  *          "get_checkbox"={
  70.  *              "method"="GET",
  71.  *              "path"="/attributes_checkbox/{id}.{_format}",
  72.  *              "identifiers"={"id"},
  73.  *              "input"=InputCheckbox::class,
  74.  *              "output"=OutputCheckbox::class,
  75.  *              "openapi_context"={
  76.  *                  "summary"="Retrieve a global yes/no attribute",
  77.  *                  "parameters"={
  78.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  79.  *                  }
  80.  *              }
  81.  *          },
  82.  *          "put_checkbox"={
  83.  *              "method"="PUT",
  84.  *              "path"="/attributes_checkbox/{id}.{_format}",
  85.  *              "identifiers"={"id"},
  86.  *              "input"=InputCheckbox::class,
  87.  *              "output"=OutputCheckbox::class,
  88.  *              "openapi_context"={
  89.  *                  "summary"="Update a global yes/no attribute",
  90.  *                  "parameters"={
  91.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  92.  *                  }
  93.  *              }
  94.  *          },
  95.  *          "delete_checkbox"={
  96.  *              "method"="DELETE",
  97.  *              "path"="/attributes_checkbox/{id}.{_format}",
  98.  *              "identifiers"={"id"},
  99.  *              "input"=InputCheckbox::class,
  100.  *              "output"=OutputCheckbox::class,
  101.  *              "openapi_context"={
  102.  *                  "summary"="Delete a global yes/no attribute",
  103.  *                  "parameters"={
  104.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  105.  *                  }
  106.  *              }
  107.  *          },
  108.  *          "get_select"={
  109.  *              "method"="GET",
  110.  *              "path"="/attributes_select/{id}.{_format}",
  111.  *              "identifiers"={"id"},
  112.  *              "input"=InputSelect::class,
  113.  *              "output"=OutputSelect::class,
  114.  *              "openapi_context"={
  115.  *                  "summary"="Retrieve a global plain field attribute",
  116.  *                  "parameters"={
  117.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  118.  *                  }
  119.  *              }
  120.  *          },
  121.  *          "put_select"={
  122.  *              "method"="PUT",
  123.  *              "path"="/attributes_select/{id}.{_format}",
  124.  *              "identifiers"={"id"},
  125.  *              "input"=InputSelect::class,
  126.  *              "output"=OutputSelect::class,
  127.  *              "openapi_context"={
  128.  *                  "summary"="Update a global plain field attribute",
  129.  *                  "parameters"={
  130.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  131.  *                  }
  132.  *              }
  133.  *          },
  134.  *          "delete_select"={
  135.  *              "method"="DELETE",
  136.  *              "path"="/attributes_select/{id}.{_format}",
  137.  *              "identifiers"={"id"},
  138.  *              "input"=InputSelect::class,
  139.  *              "output"=OutputSelect::class,
  140.  *              "openapi_context"={
  141.  *                  "summary"="Delete a global plain field attribute",
  142.  *                  "parameters"={
  143.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  144.  *                  }
  145.  *              }
  146.  *          },
  147.  *          "get_hidden"={
  148.  *              "method"="GET",
  149.  *              "path"="/attributes_hidden/{id}.{_format}",
  150.  *              "identifiers"={"id"},
  151.  *              "input"=InputHidden::class,
  152.  *              "output"=OutputHidden::class,
  153.  *              "openapi_context"={
  154.  *                  "summary"="Retrieve a global hidden attribute",
  155.  *                  "parameters"={
  156.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  157.  *                  }
  158.  *              }
  159.  *          },
  160.  *          "put_hidden"={
  161.  *              "method"="PUT",
  162.  *              "path"="/attributes_hidden/{id}.{_format}",
  163.  *              "identifiers"={"id"},
  164.  *              "input"=InputHidden::class,
  165.  *              "output"=OutputHidden::class,
  166.  *              "openapi_context"={
  167.  *                  "summary"="Update a global hidden attribute",
  168.  *                  "parameters"={
  169.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  170.  *                  }
  171.  *              }
  172.  *          },
  173.  *          "delete_hidden"={
  174.  *              "method"="DELETE",
  175.  *              "path"="/attributes_hidden/{id}.{_format}",
  176.  *              "identifiers"={"id"},
  177.  *              "input"=InputHidden::class,
  178.  *              "output"=OutputHidden::class,
  179.  *              "openapi_context"={
  180.  *                  "summary"="Delete a global hidden attribute",
  181.  *                  "parameters"={
  182.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  183.  *                  }
  184.  *              }
  185.  *          },
  186.  *          "product_class_based_get_text"={
  187.  *              "method"="GET",
  188.  *              "path"="/product_classes/{class_id}/attributes_text/{id}.{_format}",
  189.  *              "identifiers"={"id"},
  190.  *              "input"=InputText::class,
  191.  *              "output"=OutputText::class,
  192.  *              "openapi_context"={
  193.  *                  "summary"="Retrieve a product class textarea attribute",
  194.  *                  "parameters"={
  195.  *                     {"name"="class_id", "in"="path", "required"=true, "schema"={"type"="integer"}},
  196.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  197.  *                  }
  198.  *              }
  199.  *          },
  200.  *          "product_class_based_put_text"={
  201.  *              "method"="PUT",
  202.  *              "path"="/product_classes/{class_id}/attributes_text/{id}.{_format}",
  203.  *              "identifiers"={"id"},
  204.  *              "input"=InputText::class,
  205.  *              "output"=OutputText::class,
  206.  *              "openapi_context"={
  207.  *                  "summary"="Update a product class textarea attribute",
  208.  *                  "parameters"={
  209.  *                     {"name"="class_id", "in"="path", "required"=true, "schema"={"type"="integer"}},
  210.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  211.  *                  }
  212.  *              }
  213.  *          },
  214.  *          "product_class_based_delete_text"={
  215.  *              "method"="DELETE",
  216.  *              "path"="/product_classes/{class_id}/attributes_text/{id}.{_format}",
  217.  *              "identifiers"={"id"},
  218.  *              "input"=InputText::class,
  219.  *              "output"=OutputText::class,
  220.  *              "openapi_context"={
  221.  *                  "summary"="Delete a product class textarea attribute",
  222.  *                  "parameters"={
  223.  *                     {"name"="class_id", "in"="path", "required"=true, "schema"={"type"="integer"}},
  224.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  225.  *                  }
  226.  *              }
  227.  *          },
  228.  *          "product_class_based_get_checkbox"={
  229.  *              "method"="GET",
  230.  *              "path"="/product_classes/{class_id}/attributes_checkbox/{id}.{_format}",
  231.  *              "identifiers"={"id"},
  232.  *              "input"=InputCheckbox::class,
  233.  *              "output"=OutputCheckbox::class,
  234.  *              "openapi_context"={
  235.  *                  "summary"="Retrieve a product class yes/no attribute",
  236.  *                  "parameters"={
  237.  *                     {"name"="class_id", "in"="path", "required"=true, "schema"={"type"="integer"}},
  238.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  239.  *                  }
  240.  *              }
  241.  *          },
  242.  *          "product_class_based_put_checkbox"={
  243.  *              "method"="PUT",
  244.  *              "path"="/product_classes/{class_id}/attributes_checkbox/{id}.{_format}",
  245.  *              "identifiers"={"id"},
  246.  *              "input"=InputCheckbox::class,
  247.  *              "output"=OutputCheckbox::class,
  248.  *              "openapi_context"={
  249.  *                  "summary"="Update a product class yes/no attribute",
  250.  *                  "parameters"={
  251.  *                     {"name"="class_id", "in"="path", "required"=true, "schema"={"type"="integer"}},
  252.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  253.  *                  }
  254.  *              }
  255.  *          },
  256.  *          "product_class_based_delete_checkbox"={
  257.  *              "method"="DELETE",
  258.  *              "path"="/product_classes/{class_id}/attributes_checkbox/{id}.{_format}",
  259.  *              "identifiers"={"id"},
  260.  *              "input"=InputCheckbox::class,
  261.  *              "output"=OutputCheckbox::class,
  262.  *              "openapi_context"={
  263.  *                  "summary"="Delete a product class yes/no attribute",
  264.  *                  "parameters"={
  265.  *                     {"name"="class_id", "in"="path", "required"=true, "schema"={"type"="integer"}},
  266.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  267.  *                  }
  268.  *              }
  269.  *          },
  270.  *          "product_class_based_get_select"={
  271.  *              "method"="GET",
  272.  *              "path"="/product_classes/{class_id}/attributes_select/{id}.{_format}",
  273.  *              "identifiers"={"id"},
  274.  *              "input"=InputSelect::class,
  275.  *              "output"=OutputSelect::class,
  276.  *              "openapi_context"={
  277.  *                  "summary"="Retrieve a product class plain field attribute",
  278.  *                  "parameters"={
  279.  *                     {"name"="class_id", "in"="path", "required"=true, "schema"={"type"="integer"}},
  280.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  281.  *                  }
  282.  *              }
  283.  *          },
  284.  *          "product_class_based_put_select"={
  285.  *              "method"="PUT",
  286.  *              "path"="/product_classes/{class_id}/attributes_select/{id}.{_format}",
  287.  *              "identifiers"={"id"},
  288.  *              "input"=InputSelect::class,
  289.  *              "output"=OutputSelect::class,
  290.  *              "openapi_context"={
  291.  *                  "summary"="Update a product class plain field attribute",
  292.  *                  "parameters"={
  293.  *                     {"name"="class_id", "in"="path", "required"=true, "schema"={"type"="integer"}},
  294.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  295.  *                  }
  296.  *              }
  297.  *          },
  298.  *          "product_class_based_delete_select"={
  299.  *              "method"="DELETE",
  300.  *              "path"="/product_classes/{class_id}/attributes_select/{id}.{_format}",
  301.  *              "identifiers"={"id"},
  302.  *              "input"=InputSelect::class,
  303.  *              "output"=OutputSelect::class,
  304.  *              "openapi_context"={
  305.  *                  "summary"="Delete a product class plain field attribute",
  306.  *                  "parameters"={
  307.  *                     {"name"="class_id", "in"="path", "required"=true, "schema"={"type"="integer"}},
  308.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  309.  *                  }
  310.  *              }
  311.  *          },
  312.  *          "product_based_get_text"={
  313.  *              "method"="GET",
  314.  *              "path"="/products/{product_id}/attributes_text/{id}.{_format}",
  315.  *              "identifiers"={"id"},
  316.  *              "input"=ProductAttributeInputText::class,
  317.  *              "output"=ProductAttributeOutputText::class,
  318.  *              "openapi_context"={
  319.  *                  "summary"="Retrieve a product-specific textarea attribute",
  320.  *                  "parameters"={
  321.  *                     {"name"="product_id", "in"="path", "required"=true, "schema"={"type"="integer"}},
  322.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  323.  *                  }
  324.  *              }
  325.  *          },
  326.  *          "product_based_put_text"={
  327.  *              "method"="PUT",
  328.  *              "path"="/products/{product_id}/attributes_text/{id}.{_format}",
  329.  *              "identifiers"={"id"},
  330.  *              "input"=ProductAttributeInputText::class,
  331.  *              "output"=ProductAttributeOutputText::class,
  332.  *              "openapi_context"={
  333.  *                  "summary"="Update a product-specific textarea attribute",
  334.  *                  "parameters"={
  335.  *                     {"name"="product_id", "in"="path", "required"=true, "schema"={"type"="integer"}},
  336.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  337.  *                  }
  338.  *              }
  339.  *          },
  340.  *          "product_based_delete_text"={
  341.  *              "method"="DELETE",
  342.  *              "path"="/products/{product_id}/attributes_text/{id}.{_format}",
  343.  *              "identifiers"={"id"},
  344.  *              "input"=ProductAttributeInputText::class,
  345.  *              "output"=ProductAttributeOutputText::class,
  346.  *              "openapi_context"={
  347.  *                  "summary"="Delete a product-specific textarea attribute",
  348.  *                  "parameters"={
  349.  *                     {"name"="product_id", "in"="path", "required"=true, "schema"={"type"="integer"}},
  350.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  351.  *                  }
  352.  *              }
  353.  *          },
  354.  *          "product_based_get_checkbox"={
  355.  *              "method"="GET",
  356.  *              "path"="/products/{product_id}/attributes_checkbox/{id}.{_format}",
  357.  *              "identifiers"={"id"},
  358.  *              "input"=ProductAttributeInputCheckbox::class,
  359.  *              "output"=ProductAttributeOutputCheckbox::class,
  360.  *              "openapi_context"={
  361.  *                  "summary"="Retrieve a product-specific yes/no attribute",
  362.  *                  "parameters"={
  363.  *                     {"name"="product_id", "in"="path", "required"=true, "schema"={"type"="integer"}},
  364.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  365.  *                  }
  366.  *              }
  367.  *          },
  368.  *          "product_based_put_checkbox"={
  369.  *              "method"="PUT",
  370.  *              "path"="/products/{product_id}/attributes_checkbox/{id}.{_format}",
  371.  *              "identifiers"={"id"},
  372.  *              "input"=ProductAttributeInputCheckbox::class,
  373.  *              "output"=ProductAttributeOutputCheckbox::class,
  374.  *              "openapi_context"={
  375.  *                  "summary"="Update a product-specific yes/no attribute",
  376.  *                  "parameters"={
  377.  *                     {"name"="product_id", "in"="path", "required"=true, "schema"={"type"="integer"}},
  378.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  379.  *                  }
  380.  *              }
  381.  *          },
  382.  *          "product_based_delete_checkbox"={
  383.  *              "method"="DELETE",
  384.  *              "path"="/products/{product_id}/attributes_checkbox/{id}.{_format}",
  385.  *              "identifiers"={"id"},
  386.  *              "input"=ProductAttributeInputCheckbox::class,
  387.  *              "output"=ProductAttributeOutputCheckbox::class,
  388.  *              "openapi_context"={
  389.  *                  "summary"="Delete a product-specific yes/no attribute",
  390.  *                  "parameters"={
  391.  *                     {"name"="product_id", "in"="path", "required"=true, "schema"={"type"="integer"}},
  392.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  393.  *                  }
  394.  *              }
  395.  *          },
  396.  *          "product_based_get_select"={
  397.  *              "method"="GET",
  398.  *              "path"="/products/{product_id}/attributes_select/{id}.{_format}",
  399.  *              "identifiers"={"id"},
  400.  *              "input"=ProductAttributeInputSelect::class,
  401.  *              "output"=ProductAttributeOutputSelect::class,
  402.  *              "openapi_context"={
  403.  *                  "summary"="Retrieve a product-specific plain field attribute",
  404.  *                  "parameters"={
  405.  *                     {"name"="product_id", "in"="path", "required"=true, "schema"={"type"="integer"}},
  406.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  407.  *                  }
  408.  *              }
  409.  *          },
  410.  *          "product_based_put_select"={
  411.  *              "method"="PUT",
  412.  *              "path"="/products/{product_id}/attributes_select/{id}.{_format}",
  413.  *              "identifiers"={"id"},
  414.  *              "input"=ProductAttributeInputSelect::class,
  415.  *              "output"=ProductAttributeOutputSelect::class,
  416.  *              "openapi_context"={
  417.  *                  "summary"="Update a product-specific plain field attribute",
  418.  *                  "parameters"={
  419.  *                     {"name"="product_id", "in"="path", "required"=true, "schema"={"type"="integer"}},
  420.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  421.  *                  }
  422.  *              }
  423.  *          },
  424.  *          "product_based_delete_select"={
  425.  *              "method"="DELETE",
  426.  *              "path"="/products/{product_id}/attributes_select/{id}.{_format}",
  427.  *              "identifiers"={"id"},
  428.  *              "input"=ProductAttributeInputSelect::class,
  429.  *              "output"=ProductAttributeOutputSelect::class,
  430.  *              "openapi_context"={
  431.  *                  "summary"="Delete a product-specific plain field attribute",
  432.  *                  "parameters"={
  433.  *                     {"name"="product_id", "in"="path", "required"=true, "schema"={"type"="integer"}},
  434.  *                     {"name"="id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  435.  *                  }
  436.  *              }
  437.  *          }
  438.  *     },
  439.  *     collectionOperations={
  440.  *          "get_texts"={
  441.  *              "method"="GET",
  442.  *              "path"="/attributes_text.{_format}",
  443.  *              "identifiers"={"id"},
  444.  *              "input"=InputText::class,
  445.  *              "output"=OutputText::class,
  446.  *              "openapi_context"={
  447.  *                  "summary"="Retrieve a list of global textarea attributes",
  448.  *              }
  449.  *          },
  450.  *          "post_text"={
  451.  *              "method"="POST",
  452.  *              "path"="/attributes_text.{_format}",
  453.  *              "identifiers"={"id"},
  454.  *              "input"=InputText::class,
  455.  *              "output"=OutputText::class,
  456.  *              "controller"="xcart.api.attribute.text.controller",
  457.  *              "openapi_context"={
  458.  *                  "summary"="Create a global textarea attribute",
  459.  *              }
  460.  *          },
  461.  *          "get_checkboxes"={
  462.  *              "method"="GET",
  463.  *              "path"="/attributes_checkbox.{_format}",
  464.  *              "identifiers"={"id"},
  465.  *              "input"=InputCheckbox::class,
  466.  *              "output"=OutputCheckbox::class,
  467.  *              "openapi_context"={
  468.  *                  "summary"="Retrieve a list of global yes/no attributes",
  469.  *              }
  470.  *          },
  471.  *          "post_checkbox"={
  472.  *              "method"="POST",
  473.  *              "path"="/attributes_checkbox.{_format}",
  474.  *              "identifiers"={"id"},
  475.  *              "input"=InputCheckbox::class,
  476.  *              "output"=OutputCheckbox::class,
  477.  *              "controller"="xcart.api.attribute.checkbox.controller",
  478.  *              "openapi_context"={
  479.  *                  "summary"="Create a global yes/no attribute",
  480.  *              }
  481.  *          },
  482.  *          "get_selects"={
  483.  *              "method"="GET",
  484.  *              "path"="/attributes_select.{_format}",
  485.  *              "identifiers"={"id"},
  486.  *              "input"=InputSelect::class,
  487.  *              "output"=OutputSelect::class,
  488.  *              "openapi_context"={
  489.  *                  "summary"="Retrieve a list of global plain field attributes",
  490.  *              }
  491.  *          },
  492.  *          "post_select"={
  493.  *              "method"="POST",
  494.  *              "path"="/attributes_select.{_format}",
  495.  *              "identifiers"={"id"},
  496.  *              "input"=InputSelect::class,
  497.  *              "output"=OutputSelect::class,
  498.  *              "controller"="xcart.api.attribute.select.controller",
  499.  *              "openapi_context"={
  500.  *                  "summary"="Create a global plain field attribute",
  501.  *              }
  502.  *          },
  503.  *          "get_hiddens"={
  504.  *              "method"="GET",
  505.  *              "path"="/attributes_hidden.{_format}",
  506.  *              "identifiers"={"id"},
  507.  *              "input"=InputHidden::class,
  508.  *              "output"=OutputHidden::class,
  509.  *              "openapi_context"={
  510.  *                  "summary"="Retrieve a list of global hidden attributes",
  511.  *              }
  512.  *          },
  513.  *          "post_hidden"={
  514.  *              "method"="POST",
  515.  *              "path"="/attributes_hidden.{_format}",
  516.  *              "identifiers"={"id"},
  517.  *              "input"=InputHidden::class,
  518.  *              "output"=OutputHidden::class,
  519.  *              "controller"="xcart.api.attribute.hidden.controller",
  520.  *              "openapi_context"={
  521.  *                  "summary"="Create a global hidden attribute",
  522.  *              }
  523.  *          },
  524.  *          "product_class_based_get_texts"={
  525.  *              "method"="GET",
  526.  *              "path"="/product_classes/{class_id}/attributes_text.{_format}",
  527.  *              "identifiers"={"id"},
  528.  *              "input"=InputText::class,
  529.  *              "output"=OutputText::class,
  530.  *              "openapi_context"={
  531.  *                  "summary"="Retrieve a list of product class textarea attributes",
  532.  *                  "parameters"={
  533.  *                     {"name"="class_id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  534.  *                  }
  535.  *              }
  536.  *          },
  537.  *          "product_class_based_post_text"={
  538.  *              "method"="POST",
  539.  *              "path"="/product_classes/{class_id}/attributes_text.{_format}",
  540.  *              "identifiers"={"id"},
  541.  *              "input"=InputText::class,
  542.  *              "output"=OutputText::class,
  543.  *              "controller"="xcart.api.attribute.text.product_class_based_controller",
  544.  *              "openapi_context"={
  545.  *                  "summary"="Add a textarea attribute to a product class",
  546.  *                  "parameters"={
  547.  *                     {"name"="class_id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  548.  *                  }
  549.  *              }
  550.  *          },
  551.  *          "product_class_based_get_checkboxes"={
  552.  *              "method"="GET",
  553.  *              "path"="/product_classes/{class_id}/attributes_checkbox.{_format}",
  554.  *              "identifiers"={"id"},
  555.  *              "input"=InputCheckbox::class,
  556.  *              "output"=OutputCheckbox::class,
  557.  *              "openapi_context"={
  558.  *                  "summary"="Retrieve a list of product class yes/no attributes",
  559.  *                  "parameters"={
  560.  *                     {"name"="class_id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  561.  *                  }
  562.  *              }
  563.  *          },
  564.  *          "product_class_based_post_checkbox"={
  565.  *              "method"="POST",
  566.  *              "path"="/product_classes/{class_id}/attributes_checkbox.{_format}",
  567.  *              "identifiers"={"id"},
  568.  *              "input"=InputCheckbox::class,
  569.  *              "output"=OutputCheckbox::class,
  570.  *              "controller"="xcart.api.attribute.checkbox.product_class_based_controller",
  571.  *              "openapi_context"={
  572.  *                  "summary"="Add a yes/no attribute to a product class",
  573.  *                  "parameters"={
  574.  *                     {"name"="class_id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  575.  *                  }
  576.  *              }
  577.  *          },
  578.  *          "product_class_based_get_selects"={
  579.  *              "method"="GET",
  580.  *              "path"="/product_classes/{class_id}/attributes_select.{_format}",
  581.  *              "identifiers"={"id"},
  582.  *              "input"=InputSelect::class,
  583.  *              "output"=OutputSelect::class,
  584.  *              "openapi_context"={
  585.  *                  "summary"="Retrieve a list of product class plain field attributes",
  586.  *                  "parameters"={
  587.  *                     {"name"="class_id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  588.  *                  }
  589.  *              }
  590.  *          },
  591.  *          "product_class_based_post_select"={
  592.  *              "method"="POST",
  593.  *              "path"="/product_classes/{class_id}/attributes_select.{_format}",
  594.  *              "identifiers"={"id"},
  595.  *              "input"=InputSelect::class,
  596.  *              "output"=OutputSelect::class,
  597.  *              "controller"="xcart.api.attribute.select.product_class_based_controller",
  598.  *              "openapi_context"={
  599.  *                  "summary"="Add a plain field attribute to a product class",
  600.  *                  "parameters"={
  601.  *                     {"name"="class_id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  602.  *                  }
  603.  *              }
  604.  *          },
  605.  *          "product_based_get_texts"={
  606.  *              "method"="GET",
  607.  *              "path"="/products/{product_id}/attributes_text.{_format}",
  608.  *              "identifiers"={"id"},
  609.  *              "input"=ProductAttributeInputText::class,
  610.  *              "output"=ProductAttributeOutputText::class,
  611.  *              "openapi_context"={
  612.  *                  "summary"="Retrieve a list of product-specific textarea attributes",
  613.  *                  "parameters"={
  614.  *                     {"name"="product_id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  615.  *                  }
  616.  *              }
  617.  *          },
  618.  *          "product_based_post_text"={
  619.  *              "method"="POST",
  620.  *              "path"="/products/{product_id}/attributes_text.{_format}",
  621.  *              "identifiers"={"id"},
  622.  *              "input"=ProductAttributeInputText::class,
  623.  *              "output"=ProductAttributeOutputText::class,
  624.  *              "controller"="xcart.api.attribute.text.product_based_controller",
  625.  *              "openapi_context"={
  626.  *                  "summary"="Add a textarea attribute to a product",
  627.  *                  "parameters"={
  628.  *                     {"name"="product_id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  629.  *                  }
  630.  *              }
  631.  *          },
  632.  *          "product_based_get_checkboxes"={
  633.  *              "method"="GET",
  634.  *              "path"="/products/{product_id}/attributes_checkbox.{_format}",
  635.  *              "identifiers"={"id"},
  636.  *              "input"=ProductAttributeInputCheckbox::class,
  637.  *              "output"=ProductAttributeOutputCheckbox::class,
  638.  *              "openapi_context"={
  639.  *                  "summary"="Retrieve a list of product-specific yes/no attributes",
  640.  *                  "parameters"={
  641.  *                     {"name"="product_id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  642.  *                  }
  643.  *              }
  644.  *          },
  645.  *          "product_based_post_checkbox"={
  646.  *              "method"="POST",
  647.  *              "path"="/products/{product_id}/attributes_checkbox.{_format}",
  648.  *              "identifiers"={"id"},
  649.  *              "input"=ProductAttributeInputCheckbox::class,
  650.  *              "output"=ProductAttributeOutputCheckbox::class,
  651.  *              "controller"="xcart.api.attribute.checkbox.product_based_controller",
  652.  *              "openapi_context"={
  653.  *                  "summary"="Add a yes/no attribute to a product",
  654.  *                  "parameters"={
  655.  *                     {"name"="product_id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  656.  *                  }
  657.  *              }
  658.  *          },
  659.  *          "product_based_get_selects"={
  660.  *              "method"="GET",
  661.  *              "path"="/products/{product_id}/attributes_select.{_format}",
  662.  *              "identifiers"={"id"},
  663.  *              "input"=ProductAttributeInputSelect::class,
  664.  *              "output"=ProductAttributeOutputSelect::class,
  665.  *              "openapi_context"={
  666.  *                  "summary"="Retrieve a list of product-specific plain field attributes",
  667.  *                  "parameters"={
  668.  *                     {"name"="product_id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  669.  *                  }
  670.  *              }
  671.  *          },
  672.  *          "product_based_post_select"={
  673.  *              "method"="POST",
  674.  *              "path"="/products/{product_id}/attributes_select.{_format}",
  675.  *              "identifiers"={"id"},
  676.  *              "input"=ProductAttributeInputSelect::class,
  677.  *              "output"=ProductAttributeOutputSelect::class,
  678.  *              "controller"="xcart.api.attribute.select.product_based_controller",
  679.  *              "openapi_context"={
  680.  *                  "summary"="Add a plain field attribute to a product",
  681.  *                  "parameters"={
  682.  *                     {"name"="product_id", "in"="path", "required"=true, "schema"={"type"="integer"}}
  683.  *                  }
  684.  *              }
  685.  *          }
  686.  *     }
  687.  * )
  688.  */
  689. class Attribute extends \XLite\Model\Base\I18n
  690. {
  691.     use ExecuteCachedTrait;
  692.     /*
  693.      * Attribute types
  694.      */
  695.     public const TYPE_TEXT     'T';
  696.     public const TYPE_CHECKBOX 'C';
  697.     public const TYPE_SELECT   'S';
  698.     public const TYPE_HIDDEN   'H';
  699.     /*
  700.      * Add to new products or class’s assigns automatically with select value
  701.      */
  702.     public const ADD_TO_NEW_YES    'Y'// 'Yes'
  703.     public const ADD_TO_NEW_NO     'N'// 'NO'
  704.     public const ADD_TO_NEW_YES_NO 'B'// 'YES/NO' (BOTH)
  705.     /*
  706.      * Attribute delimiter
  707.      */
  708.     public const DELIMITER ', ';
  709.     /*
  710.      * Display modes
  711.      */
  712.     public const SELECT_BOX_MODE 'S';
  713.     public const SPECIFICATION_MODE 'P';
  714.     public const BLOCKS_MODE     'B';
  715.     /**
  716.      * @var int
  717.      *
  718.      * @ORM\Id
  719.      * @ORM\GeneratedValue (strategy="AUTO")
  720.      * @ORM\Column (type="integer", options={ "unsigned": true })
  721.      */
  722.     protected $id;
  723.     /**
  724.      * @var int
  725.      *
  726.      * @ORM\Column (type="integer")
  727.      */
  728.     protected $position 0;
  729.     /**
  730.      * Is attribute shown above the price
  731.      *
  732.      * @var bool
  733.      *
  734.      * @ORM\Column (type="boolean", options={"default":"0"})
  735.      */
  736.     protected $displayAbove false;
  737.     /**
  738.      * @var int
  739.      *
  740.      * @ORM\Column (type="integer", length=1)
  741.      */
  742.     protected $decimals 0;
  743.     /**
  744.      * @var \XLite\Model\ProductClass
  745.      *
  746.      * @ORM\ManyToOne (targetEntity="XLite\Model\ProductClass", inversedBy="attributes")
  747.      * @ORM\JoinColumn (name="product_class_id", referencedColumnName="id", onDelete="CASCADE")
  748.      */
  749.     protected $productClass;
  750.     /**
  751.      * @var \XLite\Model\AttributeGroup
  752.      *
  753.      * @ORM\ManyToOne (targetEntity="XLite\Model\AttributeGroup", inversedBy="attributes")
  754.      * @ORM\JoinColumn (name="attribute_group_id", referencedColumnName="id")
  755.      */
  756.     protected $attributeGroup;
  757.     /**
  758.      * @var \Doctrine\Common\Collections\Collection
  759.      *
  760.      * @ORM\OneToMany (targetEntity="XLite\Model\AttributeOption", mappedBy="attribute", cascade={"all"})
  761.      */
  762.     protected $attribute_options;
  763.     /**
  764.      * @var \XLite\Model\Product
  765.      *
  766.      * @ORM\ManyToOne (targetEntity="XLite\Model\Product", inversedBy="attributes")
  767.      * @ORM\JoinColumn (name="product_id", referencedColumnName="product_id", onDelete="CASCADE")
  768.      */
  769.     protected $product;
  770.     /**
  771.      * Option type
  772.      *
  773.      * @var string
  774.      *
  775.      * @ORM\Column (type="string", options={"fixed": true}, length=1)
  776.      */
  777.     protected $type self::TYPE_SELECT;
  778.     /**
  779.      * @var string
  780.      *
  781.      * @ORM\Column (type="string", options={"fixed": true}, length=1)
  782.      */
  783.     protected $displayMode '';
  784.     /**
  785.      * Add to new products or class’s assigns automatically
  786.      *
  787.      * @var bool
  788.      *
  789.      * @ORM\Column (type="string", options={"fixed": true}, length=1)
  790.      */
  791.     protected $addToNew '';
  792.     /**
  793.      * @var \Doctrine\Common\Collections\Collection
  794.      *
  795.      * @ORM\OneToMany (targetEntity="XLite\Model\AttributeProperty", mappedBy="attribute")
  796.      */
  797.     protected $attribute_properties;
  798.     /**
  799.      * @var \Doctrine\Common\Collections\Collection
  800.      *
  801.      * @ORM\OneToMany (targetEntity="XLite\Model\AttributeTranslation", mappedBy="owner", cascade={"all"})
  802.      */
  803.     protected $translations;
  804.     /**
  805.      * Return name of widget class
  806.      *
  807.      * @param string $type      Attribute type
  808.      * @param string $interface Interface (Admin | Customer) OPTIONAL
  809.      *
  810.      * @return string
  811.      */
  812.     public static function getWidgetClass($type$interface null)
  813.     {
  814.         if ($interface === null) {
  815.             $interface \XLite::isAdminZone() ? 'Admin' 'Customer';
  816.         }
  817.         return '\XLite\View\Product\AttributeValue\\'
  818.             $interface
  819.             '\\'
  820.             . static::getTypes($typetrue);
  821.     }
  822.     /**
  823.      * Return name of value class
  824.      *
  825.      * @param string $type Type
  826.      *
  827.      * @return string
  828.      */
  829.     public static function getAttributeValueClass($type)
  830.     {
  831.         return '\XLite\Model\AttributeValue\AttributeValue'
  832.             . static::getTypes($typetrue);
  833.     }
  834.     /**
  835.      * Constructor
  836.      *
  837.      * @param array $data Entity properties OPTIONAL
  838.      */
  839.     public function __construct(array $data = [])
  840.     {
  841.         $this->attribute_options = new \Doctrine\Common\Collections\ArrayCollection();
  842.         parent::__construct($data);
  843.     }
  844.     /**
  845.      * Return number of products associated with this attribute
  846.      *
  847.      * @return integer
  848.      */
  849.     public function getProductsCount()
  850.     {
  851.         return $this->getClass()->getProductsCount();
  852.     }
  853.     /**
  854.      * Return list of types or type
  855.      *
  856.      * @param string  $type              Type OPTIONAL
  857.      * @param boolean $returnServiceType Return service type OPTIONAL
  858.      *
  859.      * @return array | string
  860.      */
  861.     public static function getTypes($type null$returnServiceType false)
  862.     {
  863.         $list = [
  864.             static::TYPE_SELECT   => static::t('Plain field'),
  865.             static::TYPE_TEXT     => static::t('Textarea'),
  866.             static::TYPE_CHECKBOX => static::t('Yes/No'),
  867.             static::TYPE_HIDDEN   => static::t('Hidden field'),
  868.         ];
  869.         $listServiceTypes = [
  870.             static::TYPE_SELECT   => 'Select',
  871.             static::TYPE_TEXT     => 'Text',
  872.             static::TYPE_CHECKBOX => 'Checkbox',
  873.             static::TYPE_HIDDEN   => 'Hidden',
  874.         ];
  875.         $list $returnServiceType $listServiceTypes $list;
  876.         return $type !== null
  877.             ? ($list[$type] ?? null)
  878.             : $list;
  879.     }
  880.     /**
  881.      * Return list of 'addToNew' types
  882.      *
  883.      * @return array
  884.      */
  885.     public static function getAddToNewTypes()
  886.     {
  887.         return [
  888.             static::ADD_TO_NEW_YES,
  889.             static::ADD_TO_NEW_NO,
  890.             static::ADD_TO_NEW_YES_NO,
  891.         ];
  892.     }
  893.     /**
  894.      * Return values associated with this attribute
  895.      *
  896.      * @return list<\Xlite\Model\AttributeValue\AAttributeValue>
  897.      */
  898.     public function getAttributeValues()
  899.     {
  900.         $cnd = new \XLite\Core\CommonCell();
  901.         $cnd->attribute $this;
  902.         return Database::getRepo(static::getAttributeValueClass($this->getType()))
  903.             ->search($cnd);
  904.     }
  905.     /**
  906.      * Return number of values associated with this attribute
  907.      *
  908.      * @return integer
  909.      */
  910.     public function getAttributeValuesCount()
  911.     {
  912.         $cnd = new \XLite\Core\CommonCell();
  913.         $cnd->attribute $this;
  914.         return Database::getRepo(static::getAttributeValueClass($this->getType()))
  915.             ->search($cndtrue);
  916.     }
  917.     /**
  918.      * Set 'addToNew' value
  919.      *
  920.      * @param string|array $value Value
  921.      *
  922.      * @return void
  923.      */
  924.     public function setAddToNew($value)
  925.     {
  926.         if (
  927.             is_array($value)
  928.             && $this->getType() === static::TYPE_CHECKBOX
  929.         ) {
  930.             if (count($value) === 2) {
  931.                 $value = static::ADD_TO_NEW_YES_NO;
  932.             } elseif (count($value) === 1) {
  933.                 $value array_shift($value) ? static::ADD_TO_NEW_YES : static::ADD_TO_NEW_NO;
  934.             }
  935.         }
  936.         $this->addToNew in_array($value, static::getAddToNewTypes()) ? $value '';
  937.     }
  938.     /**
  939.      * Get 'addToNew' value
  940.      *
  941.      * @return array
  942.      */
  943.     public function getAddToNew()
  944.     {
  945.         $value null;
  946.         if ($this->getType() === static::TYPE_CHECKBOX) {
  947.             switch ($this->addToNew) {
  948.                 case static::ADD_TO_NEW_YES:
  949.                     $value = [1];
  950.                     break;
  951.                 case static::ADD_TO_NEW_NO:
  952.                     $value = [0];
  953.                     break;
  954.                 case static::ADD_TO_NEW_YES_NO:
  955.                     $value = [01];
  956.                     break;
  957.                 default:
  958.             }
  959.         }
  960.         return $value;
  961.     }
  962.     /**
  963.      * Set type
  964.      *
  965.      * @param string $type Type
  966.      *
  967.      * @return void
  968.      */
  969.     public function setType($type)
  970.     {
  971.         $types = static::getTypes();
  972.         if (isset($types[$type])) {
  973.             if (
  974.                 $this->type
  975.                 && $type != $this->type
  976.                 && $this->getId()
  977.             ) {
  978.                 foreach ($this->getAttributeOptions() as $option) {
  979.                     Database::getEM()->remove($option);
  980.                 }
  981.                 foreach ($this->getAttributeValues() as $value) {
  982.                     Database::getEM()->remove($value);
  983.                 }
  984.             }
  985.             $this->type $type;
  986.         }
  987.     }
  988.     /**
  989.      * Return product property (return new property if property does not exist)
  990.      *
  991.      * @param \XLite\Model\Product $product Product OPTIONAL
  992.      *
  993.      * @return \XLite\Model\AttributeProperty
  994.      */
  995.     public function getProperty($product)
  996.     {
  997.         return $this->executeCachedRuntime(function () use ($product) {
  998.             $property null;
  999.             if ($product->getProductId()) {
  1000.                 $product Database::getRepo(\XLite\Model\Product::class)?->find($product->getProductId());
  1001.                 $property Database::getRepo(\XLite\Model\AttributeProperty::class)?->findOneBy([
  1002.                     'product' => $product,
  1003.                     'attribute'  => $this,
  1004.                 ]);
  1005.                 if ($property === null) {
  1006.                     $property $this->getNewProperty($product);
  1007.                 }
  1008.             }
  1009.             return $property;
  1010.         }, ['getProperty'$this->getId(), $product->getProductId()]);
  1011.     }
  1012.     /**
  1013.      * Return new product property
  1014.      *
  1015.      * @param \XLite\Model\Product $product Product OPTIONAL
  1016.      *
  1017.      * @return \XLite\Model\AttributeProperty
  1018.      */
  1019.     protected function getNewProperty($product)
  1020.     {
  1021.         $result = new \XLite\Model\AttributeProperty();
  1022.         $result->setAttribute($this);
  1023.         $result->setProduct($product);
  1024.         $result->setDisplayAbove($this->getDisplayAbove());
  1025.         $this->addAttributeProperty($result);
  1026.         Database::getEM()->persist($result);
  1027.         return $result;
  1028.     }
  1029.     /**
  1030.      * Returns position
  1031.      *
  1032.      * @param \XLite\Model\Product $product Product OPTIONAL
  1033.      *
  1034.      * @return integer
  1035.      */
  1036.     public function getPosition($product null)
  1037.     {
  1038.         if ($product) {
  1039.             $result $this->getProperty($product);
  1040.             $result $result $result->getPosition() : 0;
  1041.         } else {
  1042.             $result $this->position;
  1043.         }
  1044.         return $result;
  1045.     }
  1046.     /**
  1047.      * Set the position
  1048.      *
  1049.      * @param integer|array $value
  1050.      *
  1051.      * @return void
  1052.      */
  1053.     public function setPosition($value)
  1054.     {
  1055.         if (is_array($value)) {
  1056.             $property $this->getProperty($value['product']);
  1057.             $property->setPosition($value['position']);
  1058.         } else {
  1059.             $this->position $value;
  1060.         }
  1061.     }
  1062.     /**
  1063.      * @param \XLite\Model\Product $product Product OPTIONAL
  1064.      *
  1065.      * @return integer
  1066.      */
  1067.     public function getDisplayAbove($product null)
  1068.     {
  1069.         if ($product) {
  1070.             $result $this->getProperty($product);
  1071.             $result $result $result->getDisplayAbove() : $this->displayAbove;
  1072.         } else {
  1073.             $result $this->displayAbove;
  1074.         }
  1075.         return $result;
  1076.     }
  1077.     /**
  1078.      * @param boolean|array $value
  1079.      *
  1080.      * @return void
  1081.      */
  1082.     public function setDisplayAbove($value)
  1083.     {
  1084.         if (is_array($value)) {
  1085.             $property $this->getProperty($value['product']);
  1086.             $property->setDisplayAbove($value['displayAbove']);
  1087.         } else {
  1088.             $this->displayAbove $value;
  1089.         }
  1090.     }
  1091.     /**
  1092.      * Add to new product
  1093.      *
  1094.      * @param \XLite\Model\Product $product Product
  1095.      *
  1096.      * @return void
  1097.      */
  1098.     public function addToNewProduct(\XLite\Model\Product $product)
  1099.     {
  1100.         $displayAbove $this->getDisplayAbove();
  1101.         if ($this->getAddToNew()) {
  1102.             $displayAbove count($this->getAddToNew()) > ?: $displayAbove;
  1103.             foreach ($this->getAddToNew() as $value) {
  1104.                 $av $this->createAttributeValue($product);
  1105.                 if ($av) {
  1106.                     $av->setValue($value);
  1107.                 }
  1108.             }
  1109.         } elseif ($this->getType() === static::TYPE_SELECT) {
  1110.             $attributeOptions Database::getRepo(\XLite\Model\AttributeOption::class)->findBy(
  1111.                 [
  1112.                     'attribute' => $this,
  1113.                     'addToNew'  => true,
  1114.                 ],
  1115.                 ['position' => 'ASC']
  1116.             );
  1117.             $displayAbove count($attributeOptions) > ?: $displayAbove;
  1118.             foreach ($attributeOptions as $attributeOption) {
  1119.                 $av $this->createAttributeValue($product);
  1120.                 if ($av) {
  1121.                     $av->setAttributeOption($attributeOption);
  1122.                     $av->setPosition($attributeOption->getPosition());
  1123.                 }
  1124.             }
  1125.         } elseif ($this->getType() === static::TYPE_TEXT) {
  1126.             $av $this->createAttributeValue($product);
  1127.             if ($av) {
  1128.                 $av->setEditable(false);
  1129.                 $av->setValue('');
  1130.             }
  1131.         } elseif ($this->getType() === static::TYPE_HIDDEN) {
  1132.             $attributeOption Database::getRepo(\XLite\Model\AttributeOption::class)->findOneBy(
  1133.                 [
  1134.                     'attribute' => $this,
  1135.                     'addToNew'  => true,
  1136.                 ]
  1137.             );
  1138.             if ($attributeOption) {
  1139.                 $av $this->createAttributeValue($product);
  1140.                 if ($av) {
  1141.                     $av->setAttributeOption($attributeOption);
  1142.                 }
  1143.             }
  1144.         }
  1145.         $this->setDisplayAbove(
  1146.             [
  1147.                 'product' => $product,
  1148.                 'displayAbove' => $displayAbove,
  1149.             ]
  1150.         );
  1151.     }
  1152.     /**
  1153.      * Apply changes
  1154.      *
  1155.      * @param \XLite\Model\Product $product Product
  1156.      * @param mixed                $changes Changes
  1157.      *
  1158.      * @return void
  1159.      */
  1160.     public function applyChanges(\XLite\Model\Product $product$changes)
  1161.     {
  1162.         if (
  1163.             (
  1164.                 !$this->getProductClass()
  1165.                 && !$this->getProduct()
  1166.             )
  1167.             || (
  1168.                 $this->getProductClass()
  1169.                 && $product->getProductClass()
  1170.                 && $this->getProductClass()->getId() == $product->getProductClass()->getId()
  1171.             )
  1172.             || ($this->getProduct()
  1173.                 && $this->getProduct()->getId() == $product->getId()
  1174.             )
  1175.         ) {
  1176.             $class = static::getAttributeValueClass($this->getType());
  1177.             $repo Database::getRepo($class);
  1178.             switch ($this->getType()) {
  1179.                 case static::TYPE_TEXT:
  1180.                     $this->setAttributeValue($product$changes);
  1181.                     break;
  1182.                 case static::TYPE_CHECKBOX:
  1183.                 case static::TYPE_SELECT:
  1184.                     foreach ($repo->findBy(['product' => $product'attribute' => $this]) as $av) {
  1185.                         $uniq $this->getType() === static::TYPE_CHECKBOX
  1186.                             $av->getValue()
  1187.                             : $av->getAttributeOption()->getId();
  1188.                         if (in_array($uniq$changes['deleted'])) {
  1189.                             $repo->delete($avfalse);
  1190.                         } elseif (
  1191.                             isset($changes['changed'][$uniq])
  1192.                             || isset($changes['added'][$uniq])
  1193.                         ) {
  1194.                             $data $changes['changed'][$uniq] ?? $changes['added'][$uniq];
  1195.                             if (
  1196.                                 isset($data['defaultValue'])
  1197.                                 && $data['defaultValue']
  1198.                                 && !$av->getDefaultValue()
  1199.                             ) {
  1200.                                 $pr $repo->findOneBy(
  1201.                                     [
  1202.                                         'product'      => $product,
  1203.                                         'attribute'    => $this,
  1204.                                         'defaultValue' => true
  1205.                                     ]
  1206.                                 );
  1207.                                 if ($pr) {
  1208.                                     $pr->setDefaultValue(false);
  1209.                                 }
  1210.                             }
  1211.                             $repo->update($av$data);
  1212.                             if (isset($changes['added'][$uniq])) {
  1213.                                 unset($changes['added'][$uniq]);
  1214.                             }
  1215.                         }
  1216.                     }
  1217.                     if ($changes['added']) {
  1218.                         foreach ($changes['added'] as $uniq => $data) {
  1219.                             if (
  1220.                                 isset($data['defaultValue'])
  1221.                                 && $data['defaultValue']
  1222.                             ) {
  1223.                                 $pr $repo->findOneBy(
  1224.                                     [
  1225.                                         'product'      => $product,
  1226.                                         'attribute'    => $this,
  1227.                                         'defaultValue' => true
  1228.                                     ]
  1229.                                 );
  1230.                                 if ($pr) {
  1231.                                     $pr->setDefaultValue(false);
  1232.                                 }
  1233.                             }
  1234.                             $av $this->createAttributeValue($product);
  1235.                             if ($av) {
  1236.                                 if ($this->getType() === static::TYPE_CHECKBOX) {
  1237.                                     $av->setValue($uniq);
  1238.                                 } else {
  1239.                                     $av->setAttributeOption(
  1240.                                         Database::getRepo(\XLite\Model\AttributeOption::class)->find($uniq)
  1241.                                     );
  1242.                                 }
  1243.                                 $repo->update($av$data);
  1244.                             }
  1245.                         }
  1246.                     }
  1247.                     break;
  1248.                 default:
  1249.             }
  1250.             Database::getEM()->flush();
  1251.         }
  1252.     }
  1253.     /**
  1254.      * Set attribute value
  1255.      *
  1256.      * @param \XLite\Model\Product $product Product
  1257.      * @param mixed                $data    Value
  1258.      *
  1259.      * @return void
  1260.      */
  1261.     public function setAttributeValue(\XLite\Model\Product $product$databool $flush true)
  1262.     {
  1263.         $repo Database::getRepo(
  1264.             static::getAttributeValueClass($this->getType())
  1265.         );
  1266.         $method $this->defineSetAttributeValueMethodName($data);
  1267.         $this->$method($repo$product$data$flush);
  1268.     }
  1269.     /**
  1270.      * Get attribute value
  1271.      *
  1272.      * @param \XLite\Model\Product $product  Product
  1273.      * @param boolean              $asString As string flag OPTIONAL
  1274.      *
  1275.      * @return mixed
  1276.      */
  1277.     public function getAttributeValue(\XLite\Model\Product $product$asString false)
  1278.     {
  1279.         $repo Database::getRepo(static::getAttributeValueClass($this->getType()));
  1280.         if (in_array($this->getType(), [static::TYPE_SELECT, static::TYPE_CHECKBOX, static::TYPE_HIDDEN])) {
  1281.             $attributeValue $repo->findBy(
  1282.                 ['product' => $product'attribute' => $this],
  1283.                 $this->getType() === static::TYPE_SELECT ? ['position' => 'ASC'] : null
  1284.             );
  1285.             if (
  1286.                 $attributeValue
  1287.                 && $asString
  1288.             ) {
  1289.                 if (is_array($attributeValue)) {
  1290.                     foreach ($attributeValue as $k => $v) {
  1291.                         $attributeValue[$k] = $v->asString();
  1292.                     }
  1293.                 } elseif (is_object($attributeValue)) {
  1294.                     $attributeValue $attributeValue->asString();
  1295.                 } elseif ($this->getType() === static::TYPE_CHECKBOX) {
  1296.                     $attributeValue = static::t('Yes');
  1297.                 }
  1298.             }
  1299.         } else {
  1300.             $attributeValue $repo->findOneBy(
  1301.                 ['product' => $product'attribute' => $this]
  1302.             );
  1303.             if ($attributeValue && $asString) {
  1304.                 $attributeValue $attributeValue->getValue();
  1305.             }
  1306.         }
  1307.         return $attributeValue;
  1308.     }
  1309.     /**
  1310.      * Get attribute value
  1311.      *
  1312.      * @param \XLite\Model\Product $product Product
  1313.      *
  1314.      * @return \XLite\Model\AttributeValue\AAttributeValue
  1315.      */
  1316.     public function getDefaultAttributeValue(\XLite\Model\Product $product)
  1317.     {
  1318.         $repo Database::getRepo(static::getAttributeValueClass($this->getType()));
  1319.         $attributeValue $repo->findOneBy(['product' => $product'attribute' => $this'defaultValue' => true]);
  1320.         if (!$attributeValue) {
  1321.             $attributeValue $repo->findDefaultAttributeValue(['product' => $product'attribute' => $this]);
  1322.         }
  1323.         return $attributeValue;
  1324.     }
  1325.     /**
  1326.      * This attribute is multiple or not flag
  1327.      *
  1328.      * @param \XLite\Model\Product $product Product
  1329.      *
  1330.      * @return boolean
  1331.      */
  1332.     public function isMultiple(\XLite\Model\Product $product)
  1333.     {
  1334.         $repo Database::getRepo(static::getAttributeValueClass($this->getType()));
  1335.         return (!$this->getProduct() || $this->getProduct()->getId() == $product->getId())
  1336.             && (!$this->getProductClass()
  1337.                 || ($product->getProductClass()
  1338.                     && $this->getProductClass()->getId() == $product->getProductClass()->getId()
  1339.                 )
  1340.             )
  1341.             && count($repo->findBy(['product' => $product'attribute' => $this]));
  1342.     }
  1343.     /**
  1344.      * This attribute is hidden or not flag
  1345.      *
  1346.      * @return bool
  1347.      */
  1348.     public function isHidden()
  1349.     {
  1350.         return $this->getType() === static::TYPE_HIDDEN;
  1351.     }
  1352.     /**
  1353.      * Create attribute value
  1354.      *
  1355.      * @param \XLite\Model\Product $product Product
  1356.      *
  1357.      * @return mixed
  1358.      */
  1359.     protected function createAttributeValue(\XLite\Model\Product $product)
  1360.     {
  1361.         $class = static::getAttributeValueClass($this->getType());
  1362.         $attributeValue = new $class();
  1363.         $attributeValue->setProduct($product);
  1364.         $attributeValue->setAttribute($this);
  1365.         Database::getEM()->persist($attributeValue);
  1366.         return $attributeValue;
  1367.     }
  1368.     /**
  1369.      * Create attribute option
  1370.      *
  1371.      * @param string $value Option name
  1372.      *
  1373.      * @return \XLite\Model\AttributeOption
  1374.      */
  1375.     protected function createAttributeOption($value)
  1376.     {
  1377.         $attributeOption = new \XLite\Model\AttributeOption();
  1378.         $attributeOption->setAttribute($this);
  1379.         $attributeOption->setName($value);
  1380.         Database::getEM()->persist($attributeOption);
  1381.         return $attributeOption;
  1382.     }
  1383.     // {{{ Set attribute value
  1384.     /**
  1385.      * Define method name for 'setAttributeValue' operation
  1386.      *
  1387.      * @param mixed $data Data
  1388.      *
  1389.      * @return string
  1390.      */
  1391.     protected function defineSetAttributeValueMethodName($data)
  1392.     {
  1393.         if ($this->getType() === static::TYPE_SELECT) {
  1394.             $result 'setAttributeValueSelect';
  1395.         } elseif ($this->getType() === static::TYPE_CHECKBOX && isset($data['multiple']) && $data['multiple']) {
  1396.             $result 'setAttributeValueCheckbox';
  1397.         } elseif ($this->getType() === static::TYPE_HIDDEN) {
  1398.             $result 'setAttributeValueHidden';
  1399.         } else {
  1400.             $result 'setAttributeValueDefault';
  1401.         }
  1402.         return $result;
  1403.     }
  1404.     /**
  1405.      * Set attribute value (select)
  1406.      *
  1407.      * @param \XLite\Model\Repo\ARepo $repo    Repository
  1408.      * @param \XLite\Model\Product    $product Product
  1409.      * @param array                   $data    Data
  1410.      *
  1411.      * @return void
  1412.      */
  1413.     protected function setAttributeValueSelect(
  1414.         \XLite\Model\Repo\ARepo $repo,
  1415.         \XLite\Model\Product $product,
  1416.         array $data,
  1417.         bool $flush true
  1418.     ) {
  1419.         $ids = [];
  1420.         $values $data['value'] ?? [];
  1421.         krsort($values);
  1422.         foreach ($values as $id => $value) {
  1423.             $value trim($value);
  1424.             if (strlen($value) > && is_int($id)) {
  1425.                 if (!isset($data['deleteValue'][$id])) {
  1426.                     [$avId] = $this->setAttributeValueSelectItem($repo$product$data$id$value$flush);
  1427.                     $ids[$avId] = $avId;
  1428.                 }
  1429.                 if (!isset($data['multiple'])) {
  1430.                     break;
  1431.                 }
  1432.             }
  1433.         }
  1434.         foreach ($repo->findBy(['product' => $product'attribute' => $this]) as $data) {
  1435.             if ($data->getId() && !isset($ids[$data->getId()])) {
  1436.                 $repo->delete($datafalse);
  1437.             }
  1438.         }
  1439.     }
  1440.     /**
  1441.      * Set select attribute item
  1442.      *
  1443.      * @param \XLite\Model\Repo\ARepo $repo    Repository
  1444.      * @param \XLite\Model\Product    $product Product
  1445.      * @param array                   $data    Data
  1446.      * @param integer                 $id      Attribute value ID
  1447.      * @param mixed                   $value   Attribute value
  1448.      *
  1449.      * @return array
  1450.      */
  1451.     protected function setAttributeValueSelectItem(
  1452.         \XLite\Model\Repo\ARepo $repo,
  1453.         \XLite\Model\Product $product,
  1454.         array $data,
  1455.         $id,
  1456.         $value,
  1457.         bool $flush true
  1458.     ) {
  1459.         $result = [nullnullnull];
  1460.         $attributeValue $attributeOption null;
  1461.         if ($this->getProduct() && $id && !isset($data['ignoreIds'])) {
  1462.             $attributeValue $repo->find($id);
  1463.             if ($attributeValue) {
  1464.                 $attributeOption $attributeValue->getAttributeOption();
  1465.                 $attributeOption->setName($value);
  1466.             }
  1467.         }
  1468.         if (!$attributeOption) {
  1469.             $attributeOption Database::getRepo(\XLite\Model\AttributeOption::class)
  1470.                 ->findOneByNameAndAttribute($value$this);
  1471.         }
  1472.         if (!$attributeOption) {
  1473.             $attributeOption $this->createAttributeOption($value);
  1474.         } else {
  1475.             $attributeValue $repo->findOneBy(
  1476.                 [
  1477.                     'attribute_option' => $attributeOption,
  1478.                     'product' => $product,
  1479.                 ]
  1480.             );
  1481.         }
  1482.         if (!$attributeValue && $id && !isset($data['ignoreIds'])) {
  1483.             $attributeValue $repo->find($id);
  1484.         }
  1485.         if ($attributeValue) {
  1486.             $result[0] = $attributeValue->getId();
  1487.         } elseif ($attributeOption) {
  1488.             $attributeValue $this->createAttributeValue($product);
  1489.             $attributeValue->setPosition(
  1490.                 array_reduce($product->getAttributeValueS()->toArray(), function ($carry$item) {
  1491.                     /* @var \XLite\Model\AttributeValue\AttributeValueSelect $item */
  1492.                     return $item->getAttribute() === $this
  1493.                         max($carry$item->getPosition())
  1494.                         : $carry;
  1495.                 }, 0) + 10
  1496.             );
  1497.             $product->addAttributeValueS($attributeValue);
  1498.         }
  1499.         if ($attributeValue) {
  1500.             $attributeValue->setAttributeOption($attributeOption);
  1501.             $attributeValue->setDefaultValue(isset($data['default'][$id]));
  1502.             foreach ($attributeValue::getModifiers() as $modifier => $options) {
  1503.                 if (isset($data[$modifier]) && isset($data[$modifier][$id])) {
  1504.                     $attributeValue->setModifier($data[$modifier][$id], $modifier);
  1505.                 }
  1506.             }
  1507.             if ($flush) {
  1508.                 Database::getEM()->flush();
  1509.             }
  1510.             $result = [
  1511.                 $attributeValue->getId(),
  1512.                 $attributeValue,
  1513.                 $attributeOption,
  1514.             ];
  1515.         }
  1516.         return $result;
  1517.     }
  1518.     /**
  1519.      * Set attribute value (checkbox)
  1520.      *
  1521.      * @param \XLite\Model\Repo\ARepo $repo    Repository
  1522.      * @param \XLite\Model\Product    $product Product
  1523.      * @param array                   $data    Data
  1524.      *
  1525.      * @return void
  1526.      */
  1527.     protected function setAttributeValueCheckbox(
  1528.         \XLite\Model\Repo\ARepo $repo,
  1529.         \XLite\Model\Product $product,
  1530.         array $data,
  1531.         bool $flush true
  1532.     ) {
  1533.         foreach ([truefalse] as $value) {
  1534.             $this->setAttributeValueCheckboxItem($repo$product$data$value);
  1535.         }
  1536.     }
  1537.     /**
  1538.      * Set attribute value (checkbox item)
  1539.      *
  1540.      * @param \XLite\Model\Repo\ARepo $repo    Repository
  1541.      * @param \XLite\Model\Product    $product Product
  1542.      * @param array                   $data    Data
  1543.      * @param boolean|int             $value   Item value
  1544.      *
  1545.      * @return \XLite\Model\AttributeValue\AttributeValueCheckbox
  1546.      */
  1547.     protected function setAttributeValueCheckboxItem(
  1548.         \XLite\Model\Repo\ARepo $repo,
  1549.         \XLite\Model\Product $product,
  1550.         array $data,
  1551.         $value
  1552.     ) {
  1553.         $attributeValue $repo->findOneBy(
  1554.             [
  1555.                 'product'   => $product,
  1556.                 'attribute' => $this,
  1557.                 'value'     => $value,
  1558.             ]
  1559.         );
  1560.         if (!$attributeValue) {
  1561.             $attributeValue $this->createAttributeValue($product);
  1562.             $attributeValue->setValue($value);
  1563.         }
  1564.         $value = (int) $value;
  1565.         $attributeValue->setDefaultValue(isset($data['default'][$value]));
  1566.         foreach ($attributeValue::getModifiers() as $modifier => $options) {
  1567.             if (isset($data[$modifier]) && isset($data[$modifier][$value])) {
  1568.                 $attributeValue->setModifier($data[$modifier][$value], $modifier);
  1569.             }
  1570.         }
  1571.         return $attributeValue;
  1572.     }
  1573.     /**
  1574.      * Set attribute value (hidden)
  1575.      *
  1576.      * @param \XLite\Model\Repo\ARepo $repo    Repository
  1577.      * @param \XLite\Model\Product    $product Product
  1578.      * @param array                   $data    Data
  1579.      *
  1580.      * @return void
  1581.      */
  1582.     protected function setAttributeValueHidden(
  1583.         \XLite\Model\Repo\ARepo $repo,
  1584.         \XLite\Model\Product $product,
  1585.         array $data,
  1586.         bool $flush true
  1587.     ) {
  1588.         $value $data['value'] ?? [];
  1589.         if (is_array($value)) {
  1590.             $value end($value);
  1591.         }
  1592.         $value trim($value);
  1593.         if (strlen($value) != 0) {
  1594.             $this->setAttributeValueHiddenItem($repo$product$data$value$flush);
  1595.         } else {
  1596.             $attributeValue $repo->findOneBy(
  1597.                 [
  1598.                     'attribute' => $this,
  1599.                     'product' => $product,
  1600.                 ]
  1601.             );
  1602.             if ($attributeValue) {
  1603.                 $repo->delete($attributeValue);
  1604.             }
  1605.         }
  1606.     }
  1607.     /**
  1608.      * Set hidden attribute item
  1609.      *
  1610.      * @param \XLite\Model\Repo\ARepo $repo    Repository
  1611.      * @param \XLite\Model\Product    $product Product
  1612.      * @param array                   $data    Data
  1613.      * @param mixed                   $value   Attribute value
  1614.      *
  1615.      * @return \XLite\Model\AttributeValue\AttributeValueHidden
  1616.      */
  1617.     protected function setAttributeValueHiddenItem(
  1618.         \XLite\Model\Repo\ARepo $repo,
  1619.         \XLite\Model\Product $product,
  1620.         array $data,
  1621.         $value,
  1622.         bool $flush true
  1623.     ) {
  1624.         $attributeValue $repo->findOneBy(
  1625.             [
  1626.                 'attribute' => $this,
  1627.                 'product' => $product,
  1628.             ]
  1629.         );
  1630.         $attributeOption Database::getRepo(\XLite\Model\AttributeOption::class)
  1631.             ->findOneByNameAndAttribute($value$this);
  1632.         if (!$attributeOption) {
  1633.             $attributeOption $this->createAttributeOption($value);
  1634.         }
  1635.         if (!$attributeValue) {
  1636.             $attributeValue $this->createAttributeValue($product);
  1637.             $product->addAttributeValueH($attributeValue);
  1638.         }
  1639.         if ($attributeValue) {
  1640.             $attributeValue->setAttributeOption($attributeOption);
  1641.             if ($flush) {
  1642.                 Database::getEM()->flush();
  1643.             }
  1644.         }
  1645.         return $attributeValue;
  1646.     }
  1647.     /**
  1648.      * Set attribute value (default)
  1649.      *
  1650.      * @param \XLite\Model\Repo\ARepo $repo    Repository
  1651.      * @param \XLite\Model\Product    $product Product
  1652.      * @param mixed                   $data    Data
  1653.      *
  1654.      * @return \XLite\Model\AttributeValue\AttributeValueText
  1655.      */
  1656.     protected function setAttributeValueDefault(
  1657.         \XLite\Model\Repo\ARepo $repo,
  1658.         \XLite\Model\Product $product,
  1659.         $data,
  1660.         bool $flush true
  1661.     ) {
  1662.         $editable is_array($data) && $this->getType() === static::TYPE_TEXT && isset($data['editable'])
  1663.             ? (bool) preg_match('/^1|yes|y|on$/iS'$data['editable'])
  1664.             : null;
  1665.         $value is_array($data) ? $data['value'] : $data;
  1666.         $value is_null($value) ? '' $value;
  1667.         if (is_array($value)) {
  1668.             $value array_shift($value);
  1669.         }
  1670.         $delete true;
  1671.         $attributeValue null;
  1672.         if ($value !== '' || $editable !== null || $this->getType() === static::TYPE_TEXT) {
  1673.             $attributeValue $repo->findOneBy(['product' => $product'attribute' => $this]);
  1674.             if (!$attributeValue) {
  1675.                 $attributeValue $this->createAttributeValue($product);
  1676.                 $delete false;
  1677.             }
  1678.             $attributeValue->setValue($value);
  1679.             if ($editable !== null) {
  1680.                 $attributeValue->setEditable($editable);
  1681.             }
  1682.         }
  1683.         if ($delete) {
  1684.             foreach ($repo->findBy(['product' => $product'attribute' => $this]) as $data) {
  1685.                 if (!$attributeValue || $attributeValue->getId() !== $data->getId()) {
  1686.                     $repo->delete($datafalse);
  1687.                 }
  1688.             }
  1689.         }
  1690.         return $attributeValue;
  1691.     }
  1692.     // }}}
  1693.     /**
  1694.      * Get id
  1695.      *
  1696.      * @return integer
  1697.      */
  1698.     public function getId()
  1699.     {
  1700.         return $this->id;
  1701.     }
  1702.     /**
  1703.      * Set decimals
  1704.      *
  1705.      * @param integer $decimals
  1706.      * @return Attribute
  1707.      */
  1708.     public function setDecimals($decimals)
  1709.     {
  1710.         $this->decimals $decimals;
  1711.         return $this;
  1712.     }
  1713.     /**
  1714.      * Get decimals
  1715.      *
  1716.      * @return integer
  1717.      */
  1718.     public function getDecimals()
  1719.     {
  1720.         return $this->decimals;
  1721.     }
  1722.     /**
  1723.      * Get type
  1724.      *
  1725.      * @return string
  1726.      */
  1727.     public function getType()
  1728.     {
  1729.         return $this->type;
  1730.     }
  1731.     /**
  1732.      * Get display mode
  1733.      *
  1734.      * @param \XLite\Model\Product $product Product OPTIONAL
  1735.      * @return string
  1736.      */
  1737.     public function getDisplayMode($product null)
  1738.     {
  1739.         $productId $product
  1740.             $product->getId()
  1741.             : \XLite\Core\Request::getInstance()->product_id;
  1742.         $prop $this->getProductAttributeProperty($productId);
  1743.         if ($prop && $prop->getDisplayMode()) {
  1744.             return $prop->getDisplayMode();
  1745.         }
  1746.         return $this->displayMode;
  1747.     }
  1748.     /**
  1749.      * @param $productId
  1750.      *
  1751.      * @return null|\XLite\Model\AttributeProperty
  1752.      */
  1753.     protected function getProductAttributeProperty($productId)
  1754.     {
  1755.         return $this->executeCachedRuntime(function () use ($productId) {
  1756.             $property null;
  1757.             if (
  1758.                 $productId
  1759.                 && ($product Database::getRepo(\XLite\Model\Product::class)->find($productId))
  1760.             ) {
  1761.                 $property Database::getRepo(\XLite\Model\AttributeProperty::class)->findOneBy([
  1762.                     'product' => $product,
  1763.                     'attribute'  => $this,
  1764.                 ]);
  1765.             }
  1766.             return $property;
  1767.         }, ['getProductAttributeProperty'$this->getId(), $productId]);
  1768.     }
  1769.     /**
  1770.      * Set display mode
  1771.      *
  1772.      * @param string $value
  1773.      * @param boolean $isNew New attribute flag OPTIONAL
  1774.      *
  1775.      * @return Attribute
  1776.      */
  1777.     public function setDisplayMode($value$isNew false)
  1778.     {
  1779.         if (
  1780.             $this->displayMode !== $value
  1781.             && $this->getAttributeProperties()
  1782.             && (!\XLite\Core\Request::getInstance()->product_id
  1783.                 || $isNew)
  1784.         ) {
  1785.             foreach ($this->getAttributeProperties() as $prop) {
  1786.                 $prop->setDisplayMode($value);
  1787.             }
  1788.         }
  1789.         $this->displayMode $value;
  1790.         return $this;
  1791.     }
  1792.     /**
  1793.      * Return display modes
  1794.      *
  1795.      * @return array
  1796.      */
  1797.     public static function getDisplayModes()
  1798.     {
  1799.         return [
  1800.             static::SELECT_BOX_MODE    => static::t('Selectbox'),
  1801.             static::BLOCKS_MODE        => static::t('Blocks'),
  1802.             static::SPECIFICATION_MODE => static::t('Specification'),
  1803.         ];
  1804.     }
  1805.     /**
  1806.      * Return display mode name
  1807.      *
  1808.      * @return string
  1809.      */
  1810.     public function getDisplayModeName()
  1811.     {
  1812.         $displayModes self::getDisplayModes();
  1813.         return $displayModes[$this->displayMode] ?? '';
  1814.     }
  1815.     /**
  1816.      * Set productClass
  1817.      *
  1818.      * @param \XLite\Model\ProductClass $productClass
  1819.      * @return Attribute
  1820.      */
  1821.     public function setProductClass(\XLite\Model\ProductClass $productClass null)
  1822.     {
  1823.         $this->productClass $productClass;
  1824.         return $this;
  1825.     }
  1826.     /**
  1827.      * Get productClass
  1828.      *
  1829.      * @return \XLite\Model\ProductClass
  1830.      */
  1831.     public function getProductClass()
  1832.     {
  1833.         return $this->productClass;
  1834.     }
  1835.     /**
  1836.      * Set attributeGroup
  1837.      *
  1838.      * @param \XLite\Model\AttributeGroup $attributeGroup
  1839.      * @return Attribute
  1840.      */
  1841.     public function setAttributeGroup(\XLite\Model\AttributeGroup $attributeGroup null)
  1842.     {
  1843.         $this->attributeGroup $attributeGroup;
  1844.         return $this;
  1845.     }
  1846.     /**
  1847.      * Get attributeGroup
  1848.      *
  1849.      * @return \XLite\Model\AttributeGroup
  1850.      */
  1851.     public function getAttributeGroup()
  1852.     {
  1853.         return $this->attributeGroup;
  1854.     }
  1855.     /**
  1856.      * Add attribute_options
  1857.      *
  1858.      * @param \XLite\Model\AttributeOption $attributeOptions
  1859.      * @return Attribute
  1860.      */
  1861.     public function addAttributeOptions(\XLite\Model\AttributeOption $attributeOptions)
  1862.     {
  1863.         $this->attribute_options[] = $attributeOptions;
  1864.         return $this;
  1865.     }
  1866.     /**
  1867.      * Get attribute_options
  1868.      *
  1869.      * @return \Doctrine\Common\Collections\Collection
  1870.      */
  1871.     public function getAttributeOptions()
  1872.     {
  1873.         return $this->attribute_options;
  1874.     }
  1875.     /**
  1876.      * Set product
  1877.      *
  1878.      * @param \XLite\Model\Product $product
  1879.      * @return Attribute
  1880.      */
  1881.     public function setProduct(\XLite\Model\Product $product null)
  1882.     {
  1883.         $this->product $product;
  1884.         return $this;
  1885.     }
  1886.     /**
  1887.      * Get product
  1888.      *
  1889.      * @return \XLite\Model\Product
  1890.      */
  1891.     public function getProduct()
  1892.     {
  1893.         return $this->product;
  1894.     }
  1895.     /**
  1896.      * Add attribute property
  1897.      *
  1898.      * @param \XLite\Model\AttributeProperty $attributeProperty
  1899.      * @return Attribute
  1900.      */
  1901.     public function addAttributeProperty(\XLite\Model\AttributeProperty $attributeProperty)
  1902.     {
  1903.         $this->attribute_properties[] = $attributeProperty;
  1904.         return $this;
  1905.     }
  1906.     /**
  1907.      * Get attribute_properties
  1908.      *
  1909.      * @return \Doctrine\Common\Collections\Collection
  1910.      */
  1911.     public function getAttributeProperties()
  1912.     {
  1913.         return $this->attribute_properties;
  1914.     }
  1915.     // {{{ Translation Getters / setters
  1916.     /**
  1917.      * @return string
  1918.      */
  1919.     public function getUnit()
  1920.     {
  1921.         return $this->getTranslationField(__FUNCTION__);
  1922.     }
  1923.     /**
  1924.      * @param string $unit
  1925.      *
  1926.      * @return \XLite\Model\Base\Translation
  1927.      */
  1928.     public function setUnit($unit)
  1929.     {
  1930.         return $this->setTranslationField(__FUNCTION__$unit);
  1931.     }
  1932.     // }}}
  1933. }