Adding Accessors to Tailor models

I’ve added ‘events’ to one of my websites using Tailor (which is fantastic btw) but I’d like to be able to generate Schema.org structured data for each event without duplicating the code on both the Events listing HTML and the Event detail HTML.

Had I gone down the Builder/Plugin route, I’d have added this code as an Accessor on the model, but is there any way of achieving this with Tailor?

I’ve looked into custom content fields but I’m not sure that’s going to achieve what I need.

Thanks in advance!

Hey @tillathenun

Sure, no problem.

Accessors can be defined externally by extending the model.getAttribute model event.

User::extend(function ($model) {
    $model->bindEvent('model.getAttribute', function ($attribute, $value) {
        if ($attribute === 'first_name') {
            return ucfirst($value);
        }
    });
});

Mutators can be defined externally by extending the model.setAttribute model event.

User::extend(function ($model) {
    $model->bindEvent('model.setAttribute', function ($attribute, $value) use ($model) {
        if ($attribute === 'first_name') {
            $model->attributes['first_name'] = strtolower($value);
        }
    });
});

Docs: Mutators - October CMS - 3.x (we’ve added this just now)

You may extend the EntryRecord model constructor using the extendInSection method to target a specific blueprint. The extendInSectionUuid method can also be used for more precise targeting.

EntryRecord::extendInSection('Blog\Post', function($model) {
    $model->bindEvent('model.afterDelete', function () use ($model) {
        // Model has been deleted!
    });
});

Docs: Models - October CMS - 3.x

Thanks for your speedy reply! That all makes sense, although I’ve put the code in the Plugin.php file boot method and nothing’s happening.

As a test, I’ve added the following which should in theory change my Event title to “chickens”, but it’s not working. Have I missed something obvious?

/**
     * Boot method, called right before the request route.
     *
     * @return void
     */
    public function boot()
    {

        // Add a schema attribute to the event model
        
        EntryRecord::extendInSection('Events\Event', function($model) {

            $model->bindEvent('model.setAttribute', function ($attribute, $value) use ($model) {
        
                if ($attribute === 'title') {
                    $model->attributes['title'] = 'chickens';
                }
    
            });

        });

    }

Hey @tillathenun

I’ve added this to App\Provider inside the boot method and it works:

\Tailor\Models\EntryRecord::extendInSection('Blog\Post', function($model) {
    $model->bindEvent('model.setAttribute', function ($attribute, $value) use ($model) {
        if ($attribute === 'title') {
            $model->attributes['title'] = 'chickens';
        }
    });
});

When saving the blog post in the admin panel, the title is always “chickens”.

Thanks again. I’ve now realised the issue was I was using model.setAttribute when I needed to use model.getAttribute for what I was doing. (Doh!)

Although that works fine for changing attributes, it doesn’t look like I can create a new one. Perhaps I’m going about it the wrong way, but if it was a Builder model, I’d add something like this:

public function getSchemaAttribute() {
return ['array_here'];
}

I’d then get the attribute with $event->schema.

But the following code doesn’t seem to work - it just returns null.

EntryRecord::extendInSection('Events\Event', function($model) {

            $model->bindEvent('model.getAttribute', function ($attribute, $value) use ($model) {
                $model->attributes['schema'] = ['array_here'];
            });
            
        });

I’m sure I’m missing something glaringly obvious so thanks and apologies in advance!

The getAttribute event differs slightly; you return the value instead of overwriting the attributes.

\Tailor\Models\EntryRecord::extendInSection('Blog\Post', function($model) {
    $model->bindEvent('model.getAttribute', function ($attribute, $value) use ($model) {
        if ($attribute === 'title') {
            return 'chicken2';
        }
    });
});

Thank you. Should that work on a new attribute as well, as $event->schema is still returning null for me using the following code.

EntryRecord::extendInSection('Events\Event', function($model) {

            $model->bindEvent('model.getAttribute', function ($attribute, $value) use ($model) {

                if ($attribute === 'schema') {

                    return ['array_here'];

                }

            });

        });

I’m not sure why this doesn’t work here; the code provided looks good and should work.