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() : string
2{
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() : bool
2{
3 return $this->indexable;
4}

getIndexedRoute() : string

Return the route to the indexed model

Example

1public function getIndexedRoute() : string
2{
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() : string
2{
3 return \Helix\Fabrick\Icon::DOCUMENT;
4}

searchableIndexRoute() : string;

Return the route to the index resource.

Example

1public static function searchableIndexRoute() : string
2{
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() : string
2{
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() : string
2{
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() : string
2{
3 return route('lego.pages.edit', $this);
4}

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 Sectionable
11{
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() : string
2{
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() : string
2{
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}