From 015058d1b66630826d866f5589cd53ee6c4778b7 Mon Sep 17 00:00:00 2001 From: Jason Irish Date: Tue, 31 Oct 2023 10:47:44 -0500 Subject: [PATCH] REFACTOR ProductObject (#37) Move product info to a dataobject. Product page has_one Product object. Allows products to be attached to multiple Product pages across categories. --- composer.json | 1 + ...{ProductFileAdmin.php => ProductAdmin.php} | 8 +- src/Extension/ProductFileDataExtension.php | 6 +- src/Model/Brochure.php | 2 +- src/Model/ProductObject.php | 209 ++++++++++++++++++ src/Page/Product.php | 171 +++----------- .../ProductFileDataExtensionTest.php | 2 +- tests/fixtures.yml | 6 +- 8 files changed, 256 insertions(+), 149 deletions(-) rename src/Admin/{ProductFileAdmin.php => ProductAdmin.php} (64%) create mode 100644 src/Model/ProductObject.php diff --git a/composer.json b/composer.json index e55140f..12ade88 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "bummzack/sortablefile": "^2.0", "dynamic/silverstripe-collection": "^3.0", "silverstripe/recipe-cms": "^5", + "silvershop/silverstripe-hasonefield":"^4.0", "symbiote/silverstripe-gridfieldextensions": "^4.0" }, "require-dev": { diff --git a/src/Admin/ProductFileAdmin.php b/src/Admin/ProductAdmin.php similarity index 64% rename from src/Admin/ProductFileAdmin.php rename to src/Admin/ProductAdmin.php index a7ecf27..4e307ca 100644 --- a/src/Admin/ProductFileAdmin.php +++ b/src/Admin/ProductAdmin.php @@ -3,28 +3,30 @@ namespace Dynamic\Products\Admin; use Dynamic\Products\Model\Brochure; +use Dynamic\Products\Model\ProductObject; use SilverStripe\Admin\ModelAdmin; /** * Class \Dynamic\Products\Admin\ProductFileAdmin * */ -class ProductFileAdmin extends ModelAdmin +class ProductAdmin extends ModelAdmin { /** * @var array */ private static $managed_models = array( + ProductObject::class, Brochure::class, ); /** * @var string */ - private static $url_segment = 'product-files'; + private static $url_segment = 'products'; /** * @var string */ - private static $menu_title = 'Product Files'; + private static $menu_title = 'Products'; } diff --git a/src/Extension/ProductFileDataExtension.php b/src/Extension/ProductFileDataExtension.php index fdf971a..6bbb31d 100644 --- a/src/Extension/ProductFileDataExtension.php +++ b/src/Extension/ProductFileDataExtension.php @@ -3,9 +3,9 @@ namespace Dynamic\Products\Extension; use Dynamic\Products\Model\ProductFile; +use Dynamic\Products\Model\ProductObject; use Dynamic\Products\Page\Product; use SilverStripe\Core\ClassInfo; -use SilverStripe\Dev\Debug; use SilverStripe\Forms\DropdownField; use SilverStripe\Forms\FieldList; use SilverStripe\Forms\GridField\GridField; @@ -19,7 +19,7 @@ * Class \Dynamic\Products\Extension\ProductFileDataExtension * * @property Brochure|ProductFileDataExtension $owner - * @method ManyManyList|Product[] Products() + * @method ManyManyList|ProductObject[] Products() */ class ProductFileDataExtension extends DataExtension { @@ -27,7 +27,7 @@ class ProductFileDataExtension extends DataExtension * @var array */ private static $belongs_many_many = array( - 'Products' => Product::class, + 'Products' => ProductObject::class, ); /** diff --git a/src/Model/Brochure.php b/src/Model/Brochure.php index bd69971..9e83445 100644 --- a/src/Model/Brochure.php +++ b/src/Model/Brochure.php @@ -7,7 +7,7 @@ /** * Class \Dynamic\Products\Model\Brochure * - * @method ManyManyList|Product[] Products() + * @method ManyManyList|ProductObject[] Products() * @mixin ProductFileDataExtension */ class Brochure extends ProductFile diff --git a/src/Model/ProductObject.php b/src/Model/ProductObject.php new file mode 100644 index 0000000..17a4aac --- /dev/null +++ b/src/Model/ProductObject.php @@ -0,0 +1,209 @@ + 'Varchar(255)', + 'Content' => 'HTMLText', + 'SKU' => 'Varchar(100)', + ]; + + /** + * @var array + */ + private static $has_many = [ + 'ProductPages' => Product::class, + ]; + + /** + * @var array + */ + private static $many_many = [ + 'Images' => File::class, + 'Brochures' => Brochure::class, + ]; + + /** + * @var array + */ + private static $many_many_extraFields = [ + 'Images' => [ + 'SortOrder' => 'Int', + ], + 'Brochures' => [ + 'SortOrder' => 'Int', + ], + ]; + + /** + * + */ + private static $owns = [ + 'Images', + ]; + + /** + * The relation name was established before requests for videos. + * The relation has subsequently been updated from Image::class to File::class + * to allow for additional file types such as mp4 + * + * @var array + */ + private static $allowed_images_extensions = [ + 'gif', + 'jpeg', + 'jpg', + 'png', + 'bmp', + 'ico', + 'mp4', + ]; + + /** + * + * @return void + */ + public function getCMSFields() + { + $this->beforeUpdateCMSFields(function (FieldList $fields) { + $fields->insertBefore( + 'Content', + TextField::create('SKU', 'Product SKU') + ); + + // Images tab + $images = SortableUploadField::create('Images') + ->setSortColumn('SortOrder') + ->setIsMultiUpload(true) + ->setAllowedExtensions($this->config()->get('allowed_images_extensions')) + ->setFolderName('Uploads/Products/Images'); + + $fields->addFieldsToTab('Root.Images', [ + $images, + ]); + + if ($this->ID) { + // Brochures + $brochures = $fields->dataFieldByName('Brochures'); + $config = $brochures->getConfig(); + $config->addComponents([ + new GridFieldOrderableRows('SortOrder'), + new GridFieldAddExistingSearchButton(), + ]) + ->removeComponentsByType([ + GridFieldAddExistingAutocompleter::class, + ]); + } + }); + + return parent::getCMSFields(); + } + + /** + * @return bool + */ + public function setImage() + { + if ($this->getHasImages()) { + $this->image = $this->Images()->sort('SortOrder')->first(); + } + return $this; + } + + /** + * @return mixed + */ + public function getImage() + { + if (!$this->image) { + $this->setImage(); + } + return $this->image; + } + + /** + * @return mixed + */ + public function getThumbnail() + { + if ($image = $this->getImage()) { + if ($thumb = Image::get()->byID($image->ID)) { + return $thumb->CMSThumbnail(); + } + } + + return false; + } + + /** + * @return $this + */ + public function setHasImages() + { + $this->has_images = $this->Images()->exists(); + return $this; + } + + /** + * @return mixed + */ + public function getHasImages() + { + if (!$this->has_images) { + $this->setHasImages(); + } + return $this->has_images; + } +} diff --git a/src/Page/Product.php b/src/Page/Product.php index b7f09a8..c0add65 100644 --- a/src/Page/Product.php +++ b/src/Page/Product.php @@ -2,91 +2,40 @@ namespace Dynamic\Products\Page; -use Bummzack\SortableFile\Forms\SortableUploadField; -use Dynamic\Products\Model\Brochure; -use SilverStripe\Assets\File; -use SilverStripe\Assets\Image; use SilverStripe\Forms\FieldList; -use SilverStripe\Forms\GridField\GridField; -use SilverStripe\Forms\GridField\GridFieldAddExistingAutocompleter; -use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor; -use SilverStripe\Forms\TextField; -use Symbiote\GridFieldExtensions\GridFieldAddExistingSearchButton; -use Symbiote\GridFieldExtensions\GridFieldOrderableRows; +use Dynamic\Products\Model\ProductObject; +use SilverShop\HasOneField\HasOneButtonField; /** * Class \Dynamic\Products\Page\Product * - * @property string $SKU - * @method ManyManyList|File[] Images() - * @method ManyManyList|Brochure[] Brochures() + * @property int $ProductID + * @method ProductObject Product() + * @mixin ProductDataExtension */ class Product extends \Page { - /** - * @var - */ - private $image; - - /** - * @var - */ - private $has_images; - /** * @var string */ private static $table_name = 'Product'; /** - * @var array - */ - private static $db = [ - 'SKU' => 'Varchar(100)', - ]; - - /** - * @var array - */ - private static $many_many = [ - 'Images' => File::class, - 'Brochures' => Brochure::class, - ]; - - /** - * @var array + * @var string */ - private static $many_many_extraFields = [ - 'Images' => [ - 'SortOrder' => 'Int', - ], - 'Brochures' => [ - 'SortOrder' => 'Int', - ], - ]; + private static $singular_name = 'Product Page'; /** - * + * @var string */ - private static $owns = [ - 'Images', - ]; + private static $plural_name = 'Product Pages'; /** - * The relation name was established before requests for videos. - * The relation has subsequently been updated from Image::class to File::class - * to allow for additional file types such as mp4 * * @var array */ - private static $allowed_images_extensions = [ - 'gif', - 'jpeg', - 'jpg', - 'png', - 'bmp', - 'ico', - 'mp4', + private static $has_one = [ + 'Product' => ProductObject::class, ]; /** @@ -100,101 +49,43 @@ class Product extends \Page public function getCMSFields() { $this->beforeUpdateCMSFields(function (FieldList $fields) { - $fields->insertBefore( - 'Content', - TextField::create('SKU', 'Product SKU') + $fields->addFieldToTab( + 'Root.Main', + HasOneButtonField::create( + $this->owner, + 'Product', + '' + ) ); - - // Images tab - $images = SortableUploadField::create('Images') - ->setSortColumn('SortOrder') - ->setIsMultiUpload(true) - ->setAllowedExtensions($this->config()->get('allowed_images_extensions')) - ->setFolderName('Uploads/Products/Images'); - - $fields->addFieldsToTab('Root.Images', [ - $images, - ]); - - if ($this->ID) { - // Brochures - $config = GridFieldConfig_RecordEditor::create(); - $config->addComponents([ - new GridFieldOrderableRows('SortOrder'), - new GridFieldAddExistingSearchButton(), - ]) - ->removeComponentsByType([ - GridFieldAddExistingAutocompleter::class, - ]); - - $brochures = GridField::create( - 'Brochures', - 'Brochures', - $this->Brochures()->sort('SortOrder'), - $config - ); - $fields->addFieldsToTab('Root.Files.Brochures', [ - $brochures, - ]); - } }); return parent::getCMSFields(); } /** - * @return bool + * @return bool|ProductCategory */ - public function setImage() + public function getCategory() { - if ($this->getHasImages()) { - $this->image = $this->Images()->sort('SortOrder')->first(); + if (!$this->owner->ParentID) { + return false; } - return $this; - } - - /** - * @return mixed - */ - public function getImage() - { - if (!$this->image) { - $this->setImage(); + /** @var ProductCategory $parent */ + $parent = $this->owner->Parent(); + if (!$parent instanceof ProductCategory) { + return false; } - return $this->image; - } - /** - * @return mixed - */ - public function getThumbnail() - { - if ($image = $this->getImage()) { - if ($thumb = Image::get()->byID($image->ID)) { - return $thumb->CMSThumbnail(); - } - } - - return false; - } - - /** - * @return $this - */ - public function setHasImages() - { - $this->has_images = $this->Images()->exists(); - return $this; + return $parent; } /** - * @return mixed + * @return string */ - public function getHasImages() + public function getCategoryTitle() { - if (!$this->has_images) { - $this->setHasImages(); + if ($this->getCategory()) { + return $this->getCategory()->Title; } - return $this->has_images; } } diff --git a/tests/Extension/ProductFileDataExtensionTest.php b/tests/Extension/ProductFileDataExtensionTest.php index 0abcb28..867fa55 100644 --- a/tests/Extension/ProductFileDataExtensionTest.php +++ b/tests/Extension/ProductFileDataExtensionTest.php @@ -29,7 +29,7 @@ public function testGetProductsCt() public function testGetProductsList() { $object = $this->objFromFixture(Brochure::class, 'one'); - $this->assertEquals($object->getProductsList(), 'Product One'); + $this->assertEquals($object->getProductsList(), 'Product Object One'); } /** diff --git a/tests/fixtures.yml b/tests/fixtures.yml index 9faa630..8b8ac63 100644 --- a/tests/fixtures.yml +++ b/tests/fixtures.yml @@ -40,7 +40,6 @@ Dynamic\Products\Page\ProductCategory: Dynamic\Products\Page\Product: one: Title: 'Product One' - Brochures: =>Dynamic\Products\Model\Brochure.one restrictedproduct: Title: 'Restricted Product' CanViewType: 'OnlyTheseUsers' @@ -51,6 +50,11 @@ Dynamic\Products\Page\Product: Parent: =>Dynamic\Products\Page\ProductCategory.restricted CanViewType: 'Inherit' +Dynamic\Products\Model\ProductObject: + one: + Title: 'Product Object One' + Product: =>Dynamic\Products\Page\Product.one + Brochures: =>Dynamic\Products\Model\Brochure.one Dynamic\Products\Page\ProductFileCollection: default: