Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

mmacdonald's avatar

Can I modify a model's values on a relationship before returning the results?

So I have a User model (which in this case is acting as a "student"). I also have a Classroom model which has a relationship that gets all the Users in that class.

The relationship works and looks like this:

public function students(){
        return $this->belongsToMany('App\User', 'classroom_student', 'classroom_id', 'student_id');
    }

Each user has a 'name' column which formats like this: "Firstname Lastname".

Occasionally I need to get the students with their names formatted like this: "Lastname, Firstname". I could change the table to have two separate columns, but I already have a few thousand users in there and I'm already using their full names in a bunch of places. So changing the formatting this way seems easier.

I tried creating a custom function on the Classroom model that looks like this:

public function studentsLastNameFirst(){
        $students = $this->students();
        foreach($students as $student){
            $name = $student->name;
            $nameArray = explode(' ', $name);
            $nameArray = array_reverse($nameArray);
            $name = implode(", ", $nameArray);
            $student->name = $name;
        }
        return $students;
    }

The php part isn't really a concern as I know it will split the name at the space, flip the names, and add a comma... (I tested it independently).

The issue is that on my view it just returns the 'students' relationship. Their names don't change at all... Is there a better way to do this? Do you see a glaring issue I'm missing here?

Thanks!

0 likes
10 replies
jlrdw's avatar

Put the results in a custom array or collection or even Json. Or for this one scenario only use the query Builder.

1 like
eduphp8's avatar

Just an idea

In model

function present(){
    return new App\Presenters\StudentPresenter($this);
}

Presenter.php

namespace App\Presenters;

abstract class Presenter
{
    protected $model;

    function __construct(Model $model)
    {
        $this->model = $model;
    }

    function __get($name)
    {
        if (method_exists($this, $name)) {
            return $this->{$name}();
        }

        if (property_exists($this, $name)) {
            return $this->{$name};
        }

        return $this->model->{$name};
    }
}

StudentPresenter.php

namespace App\Presenters;

class StudentPresenter extends Presenter
{
    public function lastNameFirst(){
        $name = $this->model->name;
        $nameArray = explode(' ', $name);
        $nameArray = array_reverse($nameArray);
        return implode(", ", $nameArray); 
    }
}

in view

{{$student->present()->lastNameFirst}}
1 like
mmacdonald's avatar

@swalker I like that, but I'd also need to be able to sortBy or orderBy... which I don't think a presenter would help with, as it just reformats after the collection is created, right?

That is a really interesting way to format things for Blade though... Definitely have a few other places I'm going to be implementing that, haha!

@jlrdw I tried a custom array but I get an error saying relationship method must return an object of type Relation. Working on a query builder solution now, to see if I can work it out.

Definitely turning into a real pain just for some last name sorting... Might just end up altering the table

jlrdw's avatar

You could alter the table without having the users do anything just have a update query do the work, hey good luck.

eduphp8's avatar

@mmacdonald yeah, it is wonderful if you want to just display it, for sorting I would make a custom query

1 like
jimmck's avatar

@mmacdonald It would be better to change the database. But if you must store first and last names in the same field. I hope you are enforcing a delimiter of some type such as a space ' ' or comma ','. Then as part of query you can use a string function on the name field and split the name field as needed. Also should you go the separate field route you can allow a concatenate operator (usually ||) to the fields and return a single name when needed. SQL has all of the normal string functions expected. You also write SQL to take the current field and split into 2 new fields after you add them. Keep the old field until you migrate all the existing code.

mmacdonald's avatar

@jimmck Honestly, I'm not sure why it was ever made as a single name field... I'm 99% sure it was the default table that Laravel uses when you make your initial Auth migration.

Back when I first built the site I didn't even think to change it but Laravel really should change that default away from a single column.

I'm heavily leaning in the alter table direction... I just need to make sure I find all the places I'm using the current single string way...

jlrdw's avatar

@mmacdonald do not forget to make a complete backup of everything in case something goes wrong. And I thoroughly enjoyed reading @jimmick 's answer.

1 like
Cronix's avatar

The problem with exploding on spaces is it will mess up if the persons last name contains more than one word. Exploding on spaces assumes the first and last name are each a single word, which is not always the case.

Signed,

Richard Van Dyke, Jr. (see?) ;)

1 like
mmacdonald's avatar

@Cronix Yeah, the more I've gotten into this the more I'm realizing the single name column is a really terrible idea... I've been locating all the spots I referenced 'name' on my site and it shouldn't be too hard to update everything and use two columns instead.

Just wish I'd realized it from the start... hindsight, though.

I'll keep the double last names in mind though when I do my update queries, since I'll need to check for that! Only split on the first space or something...

Thanks for the tip!

Please or to participate in this conversation.