Inheritance: extends DataObject, implements EditableEcommerceObject
 /**
  * finds or makes a ProductAttributeType, based on the lower case Name.
  *
  * @param ProductAttributeType | int $type
  * @param String $value
  * @param Boolean $create
  *
  * @return ProductAttributeType
  */
 public static function find_or_make($type, $value, $create = true, $findByID = false)
 {
     if ($type instanceof ProductAttributeType) {
         $type = $type->ID;
     }
     $cleanedValue = strtolower($value);
     if ($findByID) {
         $intValue = intval($value);
         $valueObj = ProductAttributeValue::get()->filter(array("ID" => $intValue, "TypeID" => intval($type)))->First();
         //debug::log("INT VALUE:" .$intValue."-".$type);
     } else {
         $valueObj = ProductAttributeValue::get()->where("(LOWER(\"Code\") = '{$cleanedValue}' OR LOWER(\"Value\") = '{$cleanedValue}') AND TypeID = " . intval($type))->First();
         //debug::log("CLEANED VALUE:" .$cleanedValue."-".$type);
     }
     if ($valueObj) {
         return $valueObj;
     }
     $valueObj = new ProductAttributeValue();
     $valueObj->Code = $cleanedValue;
     $valueObj->Value = $value;
     $valueObj->TypeID = $type;
     if ($create) {
         $valueObj->write();
     }
     return $valueObj;
 }
 /**
  * Finds or creates values for this type.
  *
  * @param array $values
  * @return DataObjectSet
  */
 public function convertArrayToValues(array $values)
 {
     $set = new ArrayList();
     foreach ($values as $value) {
         $val = $this->Values()->find('Value', $value);
         if (!$val) {
             //TODO: ignore case, if possible
             $val = new ProductAttributeValue();
             $val->Value = $value;
             $val->write();
         }
         $set->push($val);
     }
     return $set;
 }
 /**
  * Get all the {@link ProductAttributeValue} for a given attribute type,
  * based on this product's variations.
  *
  * @return DataList
  */
 public function possibleValuesForAttributeType($type)
 {
     if (!is_numeric($type)) {
         $type = $type->ID;
     }
     if (!$type) {
         return null;
     }
     return ProductAttributeValue::get()->innerJoin("ProductVariation_AttributeValues", "\"ProductAttributeValue\".\"ID\" = \"ProductVariation_AttributeValues\".\"ProductAttributeValueID\"")->innerJoin("ProductVariation", "\"ProductVariation_AttributeValues\".\"ProductVariationID\" = \"ProductVariation\".\"ID\"")->where("TypeID = {$type} AND \"ProductVariation\".\"ProductID\" = " . $this->owner->ID);
 }
 /**
  * Get all the {@link ProductAttributeValue} for a given attribute type,
  * based on this product's variations.
  *
  * @return DataList
  */
 public function possibleValuesForAttributeType($type)
 {
     if (!is_numeric($type)) {
         $type = $type->ID;
     }
     if (!$type) {
         return null;
     }
     $list = ProductAttributeValue::get()->innerJoin("ProductVariation_AttributeValues", "\"ProductAttributeValue\".\"ID\" = \"ProductVariation_AttributeValues\".\"ProductAttributeValueID\"")->innerJoin("ProductVariation", "\"ProductVariation_AttributeValues\".\"ProductVariationID\" = \"ProductVariation\".\"ID\"")->where("TypeID = {$type} AND \"ProductVariation\".\"ProductID\" = " . $this->owner->ID);
     if (!Product::config()->allow_zero_price) {
         $list = $list->where('"ProductVariation"."Price" > 0');
     }
     return $list;
 }
 /**
  * @param $typeID
  * @return callable
  */
 protected function getValuesClosure($typeID)
 {
     return function () use($typeID) {
         return ProductAttributeValue::get()->filter('TypeID', $typeID)->map('ID', 'Value')->toArray();
     };
 }
 function ProductAttributeValueGetPluralName()
 {
     return Convert::raw2att(ProductAttributeValue::get_plural_name());
 }
 function cleanup()
 {
     $sql = "\r\n\t\t\tSelect \"ProductAttributeTypeID\"\r\n\t\t\tFROM \"Product_VariationAttributes\"\r\n\t\t\tWHERE \"ProductID\" = " . $this->owner->ID;
     $data = DB::query($sql);
     $array = $data->keyedColumn();
     if (is_array($array) && count($array)) {
         foreach ($array as $key => $productAttributeTypeID) {
             //attribute type does not exist.
             if (!ProductAttributeType::get()->byID($productAttributeTypeID)) {
                 //delete non-existing combinations of Product_VariationAttributes (where the attribute does not exist)
                 //DB::query("DELETE FROM \"Product_VariationAttributes\" WHERE \"ProductAttributeTypeID\" = $productAttributeTypeID");
                 //non-existing product attribute values.
                 $productAttributeValues = ProductAttributeValue::get()->filter(array("TypeID" => $productAttributeTypeID));
                 if ($productAttributeValues->count()) {
                     foreach ($productAttributeValues as $productAttributeValue) {
                         $productAttributeValue->delete();
                     }
                 }
             }
         }
     }
 }
 private function addvariations()
 {
     $colourObject = ProductAttributeType::get()->where("\"Name\" = 'Colour'")->First();
     if (!$colourObject) {
         $colourObject = new ProductAttributeType();
         $colourObject->Name = "Colour";
         $colourObject->Label = "Colour";
         $colourObject->IsColour = true;
         $colourObject->Sort = 100;
         $colourObject->write();
     }
     if ($colourObject) {
         $redObject = ProductAttributeValue::get()->where("\"Value\" = 'red'")->First();
         if (!$redObject) {
             $redObject = new ProductAttributeValue();
             $redObject->Value = "red";
             $redObject->RGBCode = "ff0000";
             $redObject->ContrastRGBCode = "BFC1C1";
             $redObject->TypeID = $colourObject->ID;
             $redObject->Sort = 100;
             $redObject->write();
         }
         $blueObject = ProductAttributeValue::get()->where("\"Value\" = 'blue'")->First();
         if (!$blueObject) {
             $blueObject = new ProductAttributeValue();
             $blueObject->Value = "blue";
             $blueObject->RGBCode = "0000ff";
             $blueObject->ContrastRGBCode = "BFC1C1";
             $blueObject->TypeID = $colourObject->ID;
             $blueObject->Sort = 110;
             $blueObject->write();
         }
     } else {
         die("COULD NOT CREATE COLOUR OBJECT");
     }
     $sizeObject = ProductAttributeType::get()->filter("Name", 'Size')->First();
     if (!$sizeObject) {
         $sizeObject = new ProductAttributeType();
         $sizeObject->Name = "Size";
         $sizeObject->Label = "Size";
         $sizeObject->Sort = 110;
         $sizeObject->write();
     }
     if ($sizeObject) {
         $smallObject = ProductAttributeValue::get()->where("\"Value\" = 'S'")->First();
         if (!$smallObject) {
             $smallObject = new ProductAttributeValue();
             $smallObject->Value = "S";
             $smallObject->TypeID = $sizeObject->ID;
             $smallObject->Sort = 100;
             $smallObject->write();
         }
         $xtraLargeObject = ProductAttributeValue::get()->where("\"Value\" = 'XL'")->First();
         if (!$xtraLargeObject) {
             $xtraLargeObject = new ProductAttributeValue();
             $xtraLargeObject->Value = "XL";
             $xtraLargeObject->TypeID = $sizeObject->ID;
             $xtraLargeObject->Sort = 110;
             $xtraLargeObject->write();
         }
     } else {
         die("COULD NOT CREATE SIZE OBJECT");
     }
     $products = Product::get()->where("ClassName = 'Product'")->sort("RAND()")->limit(2);
     $this->addExamplePages(1, "products with variations (size, colour, etc...)", $products);
     if ($products->count() && $colourObject && $sizeObject) {
         $variationCombos = array(array("Size" => $xtraLargeObject, "Colour" => $redObject), array("Size" => $xtraLargeObject, "Colour" => $blueObject), array("Size" => $smallObject, "Colour" => $redObject), array("Size" => $smallObject, "Colour" => $blueObject));
         foreach ($products as $product) {
             $existingAttributeTypes = $product->VariationAttributes();
             $existingAttributeTypes->add($sizeObject);
             $existingAttributeTypes->add($colourObject);
             $this->addToTitle($product, "with variation", false);
             $product->Content .= "<p>On this page you can see two example of how you customers can add variations to their products (form / table)... In a real-life shop you would probably choose one or the other.</p>";
             $product->write();
             $product->Publish('Stage', 'Live');
             $product->flushCache();
             $descriptionOptions = array("", "Per Month", "", "", "Per Year", "This option has limited warranty");
             if (!ProductVariation::get()->where("ProductID  = " . $product->ID)->count()) {
                 foreach ($variationCombos as $variationCombo) {
                     $productVariation = new ProductVariation();
                     $productVariation->ProductID = $product->ID;
                     $productVariation->Price = $product->Price * 2;
                     $productVariation->Description = $descriptionOptions[rand(0, 5)];
                     $productVariation->ImageID = rand(0, 1) ? 0 : $this->getRandomImageID();
                     $productVariation->write();
                     $existingAttributeValues = $productVariation->AttributeValues();
                     $existingAttributeValues->add($variationCombo["Size"]);
                     $existingAttributeValues->add($variationCombo["Colour"]);
                     DB::alteration_message(" Creating variation for " . $product->Title . " // COLOUR " . $variationCombo["Colour"]->Value . " SIZE " . $variationCombo["Size"]->Value, "created");
                 }
             }
         }
     }
 }
 /**
  *
  * @param Int | ProductAttributeType
  *
  * @return DataList of ProductAttributeValues
  */
 function possibleValuesForAttributeType($type)
 {
     if ($type instanceof ProductAttributeType) {
         $typeID = $type->ID;
     } elseif ($type = ProductAttributeType::get()->byID(intval($type))) {
         $typeID = $type->ID;
     } else {
         return null;
     }
     $vals = ProductAttributeValue::get()->where("\"TypeID\" = {$typeID} AND \"ProductVariation\".\"ProductID\" = " . $this->owner->ID . "  AND \"ProductVariation\".\"AllowPurchase\" = 1")->sort(array("ProductAttributeValue.Sort" => "ASC"))->innerJoin("ProductVariation_AttributeValues", "\"ProductAttributeValue\".\"ID\" = \"ProductVariation_AttributeValues\".\"ProductAttributeValueID\"")->innerJoin("ProductVariation", "\"ProductVariation_AttributeValues\".\"ProductVariationID\" = \"ProductVariation\".\"ID\"");
     if ($this->variationFilter) {
         $vals = $vals->filter(array("ProductVariation.ID" => $this->variationFilter));
     }
     return $vals;
 }
 protected function createVariations()
 {
     $this->alterationMessage("================================================ CREATING VARIATIONS ================================================", "show");
     foreach ($this->data as $data) {
         $types = array();
         $values = array();
         $product = $data["Product"];
         $arrayForCreation = array();
         $variationFilter = array();
         $this->alterationMessage("<h1>Working out variations for " . $product->Title . "</h1>");
         //create attribute types for one product
         $this->alterationMessage("....Creating attribute types");
         foreach ($this->Config()->get("attribute_type_field_names") as $fieldKey => $fieldName) {
             $startMessage = "........Checking field {$fieldName}";
             $attributeTypeName = trim($data["Product"]->Title) . "_" . $fieldName;
             $filterArray = array("Name" => $attributeTypeName);
             $type = ProductAttributeType::get()->filter($filterArray)->first();
             if (!$type) {
                 $this->alterationMessage($startMessage . " ... creating new attribute type: " . $attributeTypeName, "created");
                 $type = new ProductAttributeType($filterArray);
                 $type->Label = $attributeTypeName;
                 $type->Sort = $fieldKey;
             } else {
                 $this->alterationMessage($startMessage . " ... \tfound existing attribute type: " . $attributeTypeName);
             }
             $this->addMoreAttributeType($type, $fieldName, $product);
             $type->write();
             $types[$fieldName] = $type;
             $product->VariationAttributes()->add($type);
         }
         //go through each variation to make the values
         $this->alterationMessage("....Creating attribute values");
         foreach ($data["VariationRows"] as $key => $row) {
             //go through each value
             foreach ($this->Config()->get("attribute_type_field_names") as $fieldName) {
                 if (!isset($row["Data"][$fieldName])) {
                     $this->alterationMessage("ERROR; {$fieldName} not set at all....", "deleted");
                     continue;
                 } elseif (!trim($row["Data"][$fieldName])) {
                     $this->alterationMessage("skipping {$fieldName} as there are no entries...");
                     continue;
                 }
                 $startMessage = "........Checking field {$fieldName}";
                 //create attribute value
                 $attributeValueName = $row["Data"][$fieldName];
                 $filterArray = array("Code" => $attributeValueName, "TypeID" => $types[$fieldName]->ID);
                 $value = ProductAttributeValue::get()->filter($filterArray)->first();
                 if (!$value) {
                     $this->alterationMessage($startMessage . "............creating new attribute value:  <strong>" . $attributeValueName . "</strong> for " . $types[$fieldName]->Name, "created");
                     $value = ProductAttributeValue::create($filterArray);
                     $value->Code = $attributeValueName;
                     $value->Value = $attributeValueName;
                 } else {
                     $this->alterationMessage($startMessage . "............found existing attribute value: <strong>" . $attributeValueName . "</strong> for " . $types[$fieldName]->Name);
                 }
                 $this->addMoreAttributeType($value, $types[$fieldName], $product);
                 $value->write();
                 $values[$fieldName] = $value;
                 //add at arrays for creation...
                 if (!isset($arrayForCreation[$types[$fieldName]->ID])) {
                     $arrayForCreation[$types[$fieldName]->ID] = array();
                 }
                 $arrayForCreation[$types[$fieldName]->ID][] = $value->ID;
                 if (!isset($variationFilters[$key])) {
                     $variationFilters[$key] = array();
                 }
                 $variationFilters[$key][$types[$fieldName]->ID] = $value->ID;
             }
         }
         //remove attribute types without values... (i.e. product only has size of colour)
         foreach ($product->VariationAttributes() as $productTypeToBeDeleted) {
             if ($productTypeToBeDeleted->Values()->count() == 0) {
                 $this->alterationMessage("....deleting attribute type with no values: " . $productTypeToBeDeleted->Title);
                 $product->VariationAttributes()->remove($productTypeToBeDeleted);
             }
         }
         $this->alterationMessage("....Creating Variations ///");
         //$this->alterationMessage("....Creating Variations From: ".print_r(array_walk($arrayForCreation, array($this, 'implodeWalk'))));
         //generate variations
         $variationAttributeValuesPerVariation = array();
         foreach ($arrayForCreation as $typeID => $variationEntry) {
             foreach ($variationEntry as $positionOfVariation => $attributeValueID) {
                 $variationAttributeValuesPerVariation[$positionOfVariation][$typeID] = $attributeValueID;
             }
         }
         foreach ($variationAttributeValuesPerVariation as $variationAttributes) {
             $variation = $product->getVariationByAttributes($variationAttributes);
             if ($variation instanceof ProductVariation) {
                 $this->alterationMessage(".... Variation " . $variation->FullName . " Already Exists ///");
             } else {
                 //2. if not, create variation with attributes
                 $className = $product->getClassNameOfVariations();
                 $newVariation = new $className(array('ProductID' => $product->ID, 'Price' => $product->Price));
                 $newVariation->setSaveParentProduct(false);
                 $newVariation->write();
                 $newVariation->AttributeValues()->addMany($variationAttributes);
                 $this->alterationMessage(".... Variation " . $newVariation->FullName . " created ///", "created");
             }
         }
         //find variations and add to VariationsRows
         foreach ($data["VariationRows"] as $key => $row) {
             $variation = $product->getVariationByAttributes($variationFilters[$key]);
             if ($variation instanceof ProductVariation) {
                 $this->alterationMessage("........Created variation, " . $variation->getTitle());
                 $this->data[$product->ID]["VariationRows"][$key]["Variation"] = $variation;
             } else {
                 $this->alterationMessage("........Could not find variation", "deleted");
             }
         }
     }
     $this->alterationMessage("================================================", "show");
 }