Product Properties
The Properties Module first appeared in Vanilo v0.5.
Most E-commerce systems name this technique as "product attributes". Laravel's Eloquent also names fields of models (entities) as "attributes". In order to prevent confusion between the two, Vanilo calls these "EAV" style attributes as "properties". Spree Commerce for example also has this naming: Product Properties
Overview
Product properties track individual attributes of a product which don’t apply to all products.
Examples of product properties are:
- material of a T-Shirt,
- color of a Smartphone,
- RAM size of a Laptop,
- alcohol % of a Spirit drink,
- whether a Fridge has display.
The property module was created so that properties can be created and assigned to any model, not just products. This description however, will focus on using properties for products.
Defining Properties
Properties are like "color", "material" or "wheel size". To create a new property you have to specify it's name and type.
use Vanilo\Properties\Models\Property;
$wheelSize = Property::create(['name' => 'Wheel Size', 'type' => 'text']);
echo $wheelSize->name;
// Wheel Size
echo $wheelSize->slug;
// wheel-size
Property Slug
The slug of a property is the URL compatible/friendly variant of the property name, intended to be used in filter URLs, APIs where brevity, human readability and machine compatibility are needed.
Eg.:
- name: Color; slug: color
- name: RAM Size; slug: ram
Example filter URL usage: http://someshop.com/cars?color=red&brand=chrysler&year=2019
The Property and the PropertyValue models use the Eloquent Sluggable package, thus slugs are autogenerated by default from the name field.
If you explicitly set the slug, no auto-generation will take place:
$property = Property::create(['name' => 'RAM Size', 'slug' => 'ram']);
echo $property->slug;
// ram
In case a slug already exists, the slug will be automatically extended to prevent duplicates:
$property1 = Property::create(['name' => 'Property']);
$property2 = Property::create(['name' => 'Property']);
echo $property1->slug;
// property
echo $property2->slug;
// property-1
Retrieving Properties
Find property by id, works the usual way:
$someProperty = Property::find(1);
To find by name:
$screenSizeProperty = Property::findOneByName('Screen Size');
Find by slug:
Property::findBySlug('screen-size');
Property Types
Properties can have types. Built-in types are:
- text
- boolean
- integer
- number
Types are applied to the values of properties.
Adding Custom Types
To add custom types you need to
- Create a class that implements the
PropertyType
interface - Register the type.
Example
namespace App;
class Stars implements \Vanilo\Properties\Contracts\PropertyType
{
public function getName(): string
{
return __('Stars (1-5)');
}
public function transformValue(string $value, ?array $settings)
{
$stars = intval($value);
if ($stars > 5) {
$stars = 5;
} elseif ($stars < 1) {
$stars = 1;
}
return $stars;
}
}
Register the type, preferably in AppServiceProvider::boot()
:
namespace App\Providers;
use App\Stars;
use Illuminate\Support\ServiceProvider;
use Vanilo\Properties\PropertyTypes;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
//...
PropertyTypes::register('stars', Stars::class);
//...
}
//...
}
Creating Property Values
Property values are like "red", "blue" or "white" for the "Color" property; "32GB", "64GB", etc for the "RAM Size" property.
Property values always belong exclusively to one Property. Ie. if you have for example "Drape Color" and "External Color" properties, and both have "black" as value, there will be two separate black value entries.
use Vanilo\Properties\Models\Property;
use Vanilo\Properties\Models\PropertyValue;
$color = Property::create(['name' => 'Color']);
$color->propertyValues()->create([
'title' => 'Red'
]);
// or
$color->propertyValues()->createMany([
['title' => 'Black'],
['title' => 'White'],
['title' => 'Yellow']
]);
// or
PropertyValue::create([
'property_id' => $color->id,
'title' => 'Blue',
'value' => 'blue'
]);
Retrieving Property Values
$colors = Property::find(['name' => 'Color']);
foreach($colors->values() as $color) {
echo $color->title . ', ';
}
// Red, Black, White, Yellow, Blue
Alternatively you can approach from the PropertyValue
model as well:
$property = Property::find(1);
$values = PropertyValue::byProperty($property)->get();
// it also works by passing the property id
$values = PropertyValue::byProperty(1)->get();
Ordering Values
Property values have a priority
field which is being used to sort
values.
To set the priority:
PropertyValue::create(['title' => '16GB', 'priority' => 15]);
PropertyValue::create(['title' => '32GB', 'priority' => 30]);
There are two options to work with the sorted values:
- Use the scopes directly,
- Use the
$property->values()
method, which gives a sorted collection of property values
Sorting Scopes
To retrieve values unsorted, just use the plain propertyValues
relationship:
// Collection of values, without sorting:
$property->propertyValues;
To retrieve values sorted:
// Get the relationship, apply the sort scope, get the results:
$property->propertyValues()->sort()->get();
// The same as above, short form:
$property->values();
// Reverse sort:
$property->propertyValues()->sortReverse()->get();
// Approaching from the PropertyValue model:
PropertyValue::byProperty($propertyId = 1)->sort()->get();
// Same as above, but reverse sorted:
PropertyValue::byProperty($propertyId = 1)->sortReverse()->get();