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

psaunders's avatar

Dynamic $hidden/$visible Attribute

I'm building a site where users can have public or private profiles according to a bit that is set in the model. Users that set themselves as private have to be very private, with not even an ID showing.

I've found setting the $hidden attribute very useful as the whole site is in Angular with an API. I lock everything down by default and then selectively enable things, the problem is I'm often having to do things like:

    $user = User::findOrFail($id);
    if(Auth::user()->can('show-user')){ //Site administrator, see whatever they want
        $user->setHidden('password');
        return $user; //User-edit admin can get all fields
    } else if (Auth::user()->id==$user->id){ // This user can see all their own details
        $user->setVisible($this->userVisible); // If the user is logged in they can edit
        return $user;
    } else if ($user->public){ // Public profile
        $user->setVisible(['first_name','last_name']);
    }

I was wondering if there is some way to setup the model so that the "hidden" attribute is dynamically controlled in the model. At least to the degree that if the user has a public profile, it's not totally locked down.

0 likes
5 replies
BrandonSurowiec's avatar
Level 14

To start, you could just throw this code into a custom function in your User Model and use that to look it up. Maybe something like User::findOrFailApi($id);. It may still be messy, but at least you are hiding it in one place.

Edit: Since you are casting this to json or an array, it will have the toJson and toArray methods that you can override in your user model.

public function toJson($options = 0) {
    $this->setAttributeVisibility(); // set visibility stuff here
    return parent::toJson();
}

And the same with toArray().

public function toArray() {
    $this->setAttributeVisibility(); // set visibility stuff here
    return parent::toArray();
}

Then add all your checks:

public function setAttributeVisibility()
{
...
}
2 likes
psaunders's avatar

Hey thanks, the toJson() is giving me an error I can't find anything on:

Declaration of App\User::toJson() must be compatible with Illuminate\Contracts\Support\Jsonable::toJson($options = 0)

toArray() doesn't seem to cause any errors, although I'm not using it yet.

BrandonSurowiec's avatar

You need the $options = 0 param in your method declaration to match the interface declaration. I updated my reply to include it.

public function toJson($options = 0) {

(You can look in the Illuminate\Contracts\Support\Jsonable.php file and you will see the interface requires that argument to be set.)

<?php namespace Illuminate\Contracts\Support;

interface Jsonable {

    /**
     * Convert the object to its JSON representation.
     *
     * @param  int  $options
     * @return string
     */
    public function toJson($options = 0);

}
psaunders's avatar

Hey, I figured that out myself and did it, but for some reason it didn't come right until I restarted today.

Seems to work quite well, here it is working with Zizaco\Entrust below. I had trouble figuring out how to override this though, for example if I want to cache the result I'd want to make sure it wasn't caching as the administrator. I ended up applying the filtering just to JSON, as toArray is called on every occasion.

    protected $visible = ['public'];
    public function setAttributeVisibility(){
        if($this->public){
            $this->setVisible(['first_name','last_name','public']);
        }
    }
    public function setLoggedIn(){
        if(Auth::user()->can('show-user')){ 
            $this->setHidden(['password']);
        } else if(Auth::user()->id==$this->id){
            $this->setVisible(['first_name','last_name','middle_name','email','ethnic_origin_id','date_of_birth','public','id']);
        }
    } 
    public function toJson($options = 0) {
        $this->setAttributeVisibility();
        return parent::toJson();
    }
    public function toArray() {
        return parent::toArray();
    }
melonsmasher's avatar

Just another suggestion here. I'm using Entrust for permissions and roles. I have some attributes on models that only certain users should be able to see. I'm running a function in the model constructor that determines if the currently authenticated user has rights to see those attributes. If they don't they're hidden from the user.

class Account extends Model
{

protected $classified = ['password', 'ssn', 'birth_date'];

/**
     * If the current user cannot read or write classified attributes then hide them
     */
    public function setClassifiedVisibility($permitted = false)
    {
        if ($user = auth()->user()) {
            try {
                $permitted = $user->can(['read-classified', 'write-classified']);
            } catch (Exception $e) {
                $permitted = false;
            }
        }

        if (!$permitted) $this->setHidden($this->classified);
    }

/**
     * Account constructor.
     */
    public function __construct($attributes = array(), $permitted = false)
    {
        parent::__construct($attributes);
        $this->setClassifiedVisibility($permitted);
    }
}

I apologize if my post is duplicated, laracast seems to post this twice on my end.

Please or to participate in this conversation.