This forum has moved to a new location and is in read-only mode. Please visit talk.octobercms.com to access the new location.

Maz
Maz

Hi, I'm storing in database a field named "day_of_the_week" which represent as an integer from 1 to 7 the day of the week (can't be more explicit :) ).

In my model I'm using a function which is native in Laravel to create a custom attribute on the model which contains the day name as a string

    public function getDayOfTheWeekAsStringAttribute() {
        $days = [
            1 => 'Monday',
            2 => 'Tuesday',
            3 => 'Wednesday',
            4 => 'Thursday',
            5 => 'Friday',
            6 => 'Saturday',
            7 => 'Sunday'
        ];

        return $days[$this->day_of_the_week];
    }

In my columns.yaml I'm using a partial path to display this custom field:

columns:
    day_of_the_week:
        label: 'romainmazb.appointment::lang.openinghours.labels.day_of_the_week'
        type: partial
        sortable: true
        path: ~/plugins/romainmazb/appointment/controllers/openinghours/partials/days_of_the_week.htm
<?= $record->day_of_the_week_as_string ?>

But of course, if I'm here, it's because that doesn't work, I tried to dd($record) and my custom attributes isn't present in the model.

How to achieve this?

And also, is there another easier way to render this data? Create a partial to render just one value is kind of ugly IMO...

Thanks for any advice!

[EDIT] I accomplished what I wanted to do with a better approach, using the lang files:

<?= Lang::get('romainmazb.appointment::lang.openinghours.labels.day' . $record->day_of_the_week); ?>

But I still want to know how to create custom calculated attributes in octobercms models?

Thanks again :]

Last updated

daftspunky
daftspunky

This code should work

<?= $record->day_of_the_week_as_string ?>

However this code will not know about your custom attribute

dd($record)

You must tell the model about your custom attribute by adding it to the $appends property

/**
 * The accessors to append to the model's array form.
 *
 * @var array
 */
protected $appends = ['day_of_the_week_as_string'];

Now it should appear on the model as a regular attribute

Last updated

Maz
Maz

daftspunk said: You must tell the model about your custom attribute by adding it to the $appends property

/**
* The accessors to append to the model's array form.
*
* @var array
*/
protected $appends = ['day_of_the_week_as_string'];

Now it should appear on the model as a regular attribute

Here was the trick! It works perfectly. Didn't think to use it since in Laravel it's for the serialization... Thanks.

Any talks about the minor question about creating a partial just to render this custom value? I know how to do it, but it seems to me to be kind of ugly, one file for one value... I thougt maybe it would be more easier to just have a field in plugin builder for this (model's attribute which does not come from the database).

[EDIT] Found the solution in the octoberCMS documentation and in the Laravel API for those who are interested:

// In your plugin's controller
public function listExtendRecords($records)
{
    // Parse the collection the field
    $parsed_records = $records->getCollection()->transform(function ($record) {
        // Apply modification, in my case day_of_the_week with string render from lang file
        $record->day_of_the_week = Lang::get('romainmazb.appointment::lang.openinghours.labels.day' . $record->day_of_the_week);
        return $record;
    });
    // Return the paginator as it was at the beginning with the modified records
    return new Paginator($parsed_records, $records->total(), $records->perPage(), $records->currentPage());
}

Then you can use in the plugin builder the field as you usually do: just fill the field name from database and it's type.

Last updated

neilcarpenter
neilcarpenter

Define it in your columns.yaml like this...

columns:
    day_of_the_week_as_string:
        label: 'romainmazb.appointment::lang.openinghours.labels.day_of_the_week'

Should work.

Maz
Maz

neilcarpenter said:

columns:
   day_of_the_week_as_string:
       label: 'romainmazb.appointment::lang.openinghours.labels.day_of_the_week'

I tried something like that before... In this case, how do I reference it into the models columns builder? I can't set the field name as day_of_the_week_as_string because it doesn't exist in database. The value from field doesn't work neither.

Last updated

neilcarpenter
neilcarpenter

I've never actually used the Builder plugin - Which i'm assuming you're talking about?

The autocomplete feature isn't showing you your custom attribute name because it's not in the database.... like you said.

However the docs say this...

The Field property column has an autocompletion feature attached. It allows you to select columns from the database table that is bound to the model. At the moment it doesn't show relation properties, but you can still type them in manually.

Which kind of makes me think you don't have to choose an option from the auto complete.

So just type out "day_of_the_week_as_string" manually and try that.

Maz
Maz

I tried:

  • modify directly the yaml file: when displaying the list, October is trying to select day_of_the_week_as_string from the table

  • Insert directly in builder day_of_the_week_as_string: can't validate, October says directly it's unable to find to field in table.

Seems like due to OctoberCMS limitation, my solution is not the better one, but the only one maybe...

It's really not the most efficient way, but it works...

// In your plugin's controller
public function listExtendRecords($records)
{
    // Parse the collection the field
    $parsed_records = $records->getCollection()->transform(function ($record) {
        // Apply modification, in my case day_of_the_week with string render from lang file
        $record->day_of_the_week = Lang::get('romainmazb.appointment::lang.openinghours.labels.day' . $record->day_of_the_week);
        return $record;
    });
    // Return the paginator as it was at the beginning with the modified records
    return new Paginator($parsed_records, $records->total(), $records->perPage(), $records->currentPage());
}

Last updated

neilcarpenter
neilcarpenter

Something isn't right because I've 100% done something similar to what you're trying to do by just defining it in my columns.yaml file and then having a getWhateverAttribute() on the model.

If you do this,

columns:
   day_of_the_week_as_string:
       label: 'romainmazb.appointment::lang.openinghours.labels.day_of_the_week'

And then have this in your model

public function getDayOfTheWeekAsStringAttribute()
{
    $days = [
        1 => 'Monday',
        2 => 'Tuesday',
        3 => 'Wednesday',
        4 => 'Thursday',
        5 => 'Friday',
        6 => 'Saturday',
        7 => 'Sunday'
    ];

    return $days[$this->day_of_the_week];
}

What error do you get when trying to view the list in the backend?

Maz
Maz

Woaw you pointed something really weird:

Using this columns.yaml:

columns:
    day_of_the_week:
        label: 'romainmazb.appointment::lang.openinghours.labels.day_of_the_week'
        type: text
        sortable: true
    open_at:
        label: 'romainmazb.appointment::lang.openinghours.labels.open_at'
        type: time
    close_at:
        label: 'romainmazb.appointment::lang.openinghours.labels.close_at'
        type: time
    day_of_the_week_as_string:
        label: 'romainmazb.appointment::lang.openinghours.labels.day_of_the_week'

You are right: all work as expected: day_of_the_week as an integer, and _as_string as a string. So I wanted to delete day_of_the_week, which becomes useless:

columns:
    open_at:
        label: 'romainmazb.appointment::lang.openinghours.labels.open_at'
        type: time
    close_at:
        label: 'romainmazb.appointment::lang.openinghours.labels.close_at'
        type: time
    day_of_the_week_as_string:
        label: 'romainmazb.appointment::lang.openinghours.labels.day_of_the_week'

Still work, but I want the day_of_the_week_as_string to render to the first column:

columns:
    day_of_the_week_as_string:
        label: 'romainmazb.appointment::lang.openinghours.labels.day_of_the_week'
    open_at:
        label: 'romainmazb.appointment::lang.openinghours.labels.open_at'
        type: time
    close_at:
        label: 'romainmazb.appointment::lang.openinghours.labels.close_at'
        type: time

Here it blows up:

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'day_of_the_week_as_string' in 'order clause' (SQL: select romainmazb_appointment_opening_hours.* from romainmazb_appointment_opening_hours order by day_of_the_week_as_string desc limit 20 offset 0)

This was why I thought it wasn't working... I always tried to place it at the first column and all my tries failed.

Why?

Using ID as the first column works, seems like October want to pass my day_of_the_week_as_string as primary key maybe?

Last updated

neilcarpenter
neilcarpenter

Ah I think it might be because you're trying to sort by day_of_the_week_as_string

Try setting sortable: false for that column in your columns.yaml

neilcarpenter
neilcarpenter

Maybe by default october orders by the first sortable column.

You can override this in your config_list.yaml by setting the defaultSort property.

# Default sorting column
defaultSort:
    column: created_at
    direction: desc

Last updated

Maz
Maz

Yeah! It was that! Thanks you neilcarpenter.

My final columns.yaml, with invisible but sortable day_of_the_week so my list is filtered by the day_of_the_week_as_string in order from Monday to Sunday:

columns:
    day_of_the_week:
        type: number
        invisible: true
    day_of_the_week_as_string:
        label: 'romainmazb.appointment::lang.openinghours.labels.day_of_the_week'
        type: text
    open_at:
        label: 'romainmazb.appointment::lang.openinghours.labels.open_at'
        type: time
    close_at:
        label: 'romainmazb.appointment::lang.openinghours.labels.close_at'
        type: time
defaultSort:
    column: day_of_the_week
    direction: asc

This is much prettier than my solution! I love that clean way :)

1-12 of 12

You cannot edit posts or make replies: the forum has moved to talk.octobercms.com.