Models
Introduction
If your app makes use of custom models, it can be very beneficial to register them with Strata. That way you get a lot on niceties of of the box automatically, such as models being searchable via the search box, indexable, publishable, and adding metafields to them.
Registering models
Models can be registered when you register your app. Just list the models you want to Strata to know about.
1return $app 2 ->name('e-commerce')3 ->models([ 4 Product::class,5 ]);
Extending
Strata provides a number of common extensions that you can implement to enhance the functionality of your model.
Sections
You may want your model to also be able to have sections in the same way pages and footers do, where
you can associate sections to a model and build up your "page" that way.
First step is to have you model implement the Helix\Lego\Models\Contracts\Sectionable
interface.
This interface require you to define a number of methods on you model. Some of these can be implemented
from the Helix\Lego\Models\Traits\HasSections
trait.
Methods needed
sections
This is the relationship MorphToMany
relationship between your model and the Helix\Lego\Models\Section
model.
By using the Helix\Lego\Models\Traits\HasSections
trait this comes free out of the box.
editorShowViewRoute
This is needed to set up routing for the editor aka. Customize. But we have a few preliminary steps we
need to take before we can find in the method.
First we need to define the route the editor for our model.
So in your backend routes file we need to add a GET
route for the editor.
Example: The route needs the sectionable model and the editor_view?
binding.
Now in your controller you create that editor
method.
1Route::group([2 'as' => 'products.',3 'prefix' => 'products',4 'middleware' => ['enabled:Astrogoat\Shopify\Settings\ShopifySettings']5], function () {6 Route::get('/', [ProductsController::class, 'index'])->name('index');7 // Other CRUD routes usually go here.8 Route::get('{product}/editor/{editor_view?}', [ProductsController::class, 'editor'])->name('editor'); 9});
Make sure to lazy load the sections' relationship. Next you return a view provided by Strata, just make sure to pass your model as the sectionable.
1<?php 2 3class ProductsController extends Controller 4{ 5 // ... 6 public function editor(Product $product, $editorView = 'editor') 7 { 8 $product->load('sections'); 9 10 return view('lego::editor.editor', [11 'sectionable' => $product,12 'editorView' => $editorView,13 ]);14 }15}
Now we can fill in the editorShowViewRoute
method which needs to return the route we just created.
1public function editorShowViewRoute(string $layout = null) : string 2{ 3 // Replace "products" with you route name. 4 // vvvvvvvv 5 return route('lego.products.editor', [ 6 'product' => $this, // Replace 'product' with your route model binding name. 7 'editor_view' => 'show', // Keep 8 'layout' => $layout, // Keep 9 ]);10}
getDisplayKeyName
This is the database column name of the column you want to be used when Strata needs to display the title/name of you model.
Example:
Let's say you have a model called Product
with a migration and you store the title of the product
in a column called title
.
1Schema::create('products', function (Blueprint $table) {2 $table->increments('id');3 $table->string('layout')->nullable();4 $table->string('title'); 5 $table->dateTime('created_at')->nullable();6 $table->dateTime('updated_at')->nullable();7});
Then you would return the name of the column in the getDisplayKeyName
method.
1public static function getDisplayKeyName() : string2{3 return 'title';4}
Metafields
Make your model have the ability to define metafields is fairly straightforward. Start by implementing
the Helix\Lego\Models\Contracts\Metafieldable
interface and use the Helix\Lego\Models\Traits\HasMetafields
trait. And... that's all there is to it.
Publishable
Documentation to come.
Indexable
Indexable can be used on your models to let Strata know if your model should be indexed by search engines. If it should be indexed, your model will be also be included in sitemaps. If your model also implement the "Publishable" contract then your model's published state will be taking into account. E.g. your model implements both "Indexable" and "Publishable" but your model hasn't been marked as published, then it won't be included in sitemaps and will not be indexed by search engines.
Methods needed
shouldIndex() : bool
Return your business logic if the model should index or not.
Example
1public function shouldIndex() : bool2{3 return $this->indexable;4}
getIndexedRoute() : string
Return the route to the indexed model
Example
1public function getIndexedRoute() : string2{3 return route('products.show', $this);4}
Searchable
Implement the "Searchable" contract to have your models show up when you search in admin.
Methods needed
searchableIcon() : string;
The icon to show up in the search results. Should be the name of a Heroicons icon provided through
Fabrick. See \Helix\Fabrick\Icon::class
Example
1public static function searchableIcon() : string2{3 return \Helix\Fabrick\Icon::DOCUMENT;4}
searchableIndexRoute() : string;
Return the route to the index resource.
Example
1public static function searchableIndexRoute() : string2{3 return route('lego.pages.index');4}
scopeGlobalSearch($query, $value);
Here you can scope the query used to search for models to include in the search results. The first argument is the query builder and the second is the search query, i.e. what the user typed and is searching for.
Example
1public function scopeGlobalSearch($query, $value)2{3 return $query->where('title', 'LIKE', "%{$value}%");4}
searchableName() : string;
Here you can define what should be return to show as the title for your model in the search results.
Example
1public function searchableName() : string2{3 return $this->title;4}
searchableDescription() : string;
Just like searchableName
this is where you can define what the description should be in the search
results.
Example
1public function searchableDescription() : string2{3 return $this->meta['description'] ?? '';4}
searchableRoute() : string;
This defines the route to where the user is redirect when clicked on the search result.
Example
1public function searchableRoute() : string2{3 return route('lego.pages.edit', $this);4}
Footer
Models can also have a footer attached to them. Make sure your include the HasFooter
trait on your model.
Example
1<?php 2 3namespace Astrogoat\Locations\Models; 4 5use Helix\Lego\Models\Contracts\Sectionable; 6use Helix\Lego\Models\Model; 7use Helix\Lego\Models\Traits\HasFooter; 8use Helix\Lego\Models\Traits\HasSections; 9 10class Location extends Model implements Sectionable11{12 use HasFooter; 13 use HasSections;
CRUD Actions
Adding CRUD (Create, Read, Update, and Delete) actions to your models can often be a tedious process so Strata aims to make it easier and as an added benefit it also standardizes model pages.
Create and Edit
Strata provide a Livewire Form
class that can be used for both Create and Edit actions.
Start by creating a new Form component for your model that extends Helix\Lego\Http\Livewire\Models\Form
. It will require you to add two methods.
model()
This should return the class string of the model you're working with. I.e. for pages it would be
1public function model() : string2{3 return Helix\Lego\Models\Page::class;4}
view()
This should be the name of the Blade view that contains the form. I.e. again for pages it would be:
1public function view() : string2{3 return 'lego::models.pages.form';4}
Lastly we need to set the model for the form to work. So in the mount()
method you will be accepting the model ID as the parameter. I.e. if we continue with the pages example it would be
1public function mount($page = null)2{3 $this->setModel($page);4}
The name if the parameter is important as it has to match our route bindings. So in your backend.php
routes file you define your route like so:
1Route::group([2 'as' => 'pages.',3 'prefix' => 'pages',4], function () {5 // ...6 Route::get('create', Models\Pages\Form::class)->name('create');7 Route::get('{page}/edit', Models\Pages\Form::class)->name('edit');8 // ...9});
Blade
The Blade view you defined in the view()
method will hold our markup. It will as get your model injected as $model
.
Model bindings/Rules
For Livewire to set your two-way model bindings they need to be defined in the rules()
method and each binding should be prefixed with model.
. I.e. for pages we have:
1public function rules() 2{ 3 return [ 4 'model.title' => 'required', 5 'model.slug' => [new SlugRule($this->model)], 6 'model.meta.description' => 'nullable', 7 'model.indexable' => 'nullable', 8 'model.canonical_page_id' => 'nullable', 9 'model.layout' => 'nullable',10 'model.footer_id' => 'nullable',11 'model.published_at' => 'nullable',12 'model.is_landing_page' => 'boolean',13 'model.category' => 'nullable',14 ];15}