Dynamically hide certain columns when returning an

2019-01-31 21:25发布

问题:

How do dynamically hide certain columns when returning an Eloquent object as JSON? E.g. to hide the 'password' column:

$users = User::all();
return Response::json($users);

I'm aware I can set protected properties in the model ($hidden or $visible), but how do I set these dynamically? I might want to hide or show different columns in different contexts.

回答1:

$model->getHidden();
$model->setHidden(array $columns);

$model->setVisible(array $columns);


回答2:

I've found a complete solution around the problem with using $model->setHidden(array $columns);

Lets say, for example, that you would like to decide in the controller exactly which fields to return. Updating only the model's hidden forces you to go over each model before you return an array of models for example. The problem becomes even worse when those models have relationships that you would also like to change. You have to loop over each model, set the hidden attribute, and then for each also set the relationships hidden. What a mess.

My solution involves creating a static member for each model that when present, updates the visible/hidden attribute just before the call to "toArray":

<?php

trait DynamicHiddenVisible {

    public static $_hidden = null;
    public static $_visible = null;

    public static function setStaticHidden(array $value) {
        self::$_hidden = $value;
        return self::$_hidden;
    }

    public static function getStaticHidden() {
        return self::$_hidden;
    }

    public static function setStaticVisible(array $value) {
        self::$_visible = $value;
        return self::$_visible;
    }

    public static function getStaticVisible() {
        return self::$_visible;
    }

    public static function getDefaultHidden() {
        return with(new static)->getHidden();
    }

    public static function geDefaultVisible() {
        return with(new static)->getVisible();
    }

    public function toArray()    {
        if (self::getStaticVisible())
            $this->visible = self::getStaticVisible();
        else if (self::getStaticHidden())
            $this->hidden = self::getStaticHidden();
        return parent::toArray();
    }

}

As an added bonus, I expose a way to the model's default hidden/visible that you may have set in your model's class.

Don't to forget to add the trait

class Client extends Eloquent {
     use DynamicHiddenVisible;
}

Finally, in the controller, before returning your model, decide on visible/hidden attributes:

public function getIndex($clientId) {
    // in this specific call, I would like to hide the "special_type" field of my Client model
    $hiddenFields = Client::getDefaultHidden();
    array_push($hiddenFields, "special_type");
    Client::setStaticHidden($hiddenFields);

    return Client::find($clientId)->toJson();
}


回答3:

From Lavarel 5.3 Documentation :

Temporarily Modifying Attribute Visibility

If you would like to make some typically hidden attributes visible on a given model instance, you may use the makeVisible method. The makeVisible method returns the model instance for convenient method chaining:

return $user->makeVisible('attribute')->toArray();

Likewise, if you would like to make some typically visible attributes hidden on a given model instance, you may use the makeHidden method.

return $user->makeHidden('attribute')->toArray();


回答4:

I don't believe it is the job of the ORM to worry about presentation logic, and that is what JSON is. You'll aways need to cast data to various types as well as hide things and sometimes create a buffer zone to rename things safely.

You can do all of that with Fractal which I built for exactly this reason.

<?php namespace App\Transformer;

use Acme\Model\Book;
use League\Fractal\TransformerAbstract;

class BookTransformer extends TransformerAbstract
{
    /**
     * List of resources possible to include
     *
     * @var array
     */
    protected $availableIncludes = [
        'author'
    ];

    /**
     * Turn this item object into a generic array
     *
     * @return array
     */
    public function transform(Book $book)
    {
        return [
            'id'    => (int) $book->id,
            'title' => $book->title,
            'year'    => (int) $book->yr,
            'links'   => [
                [
                    'rel' => 'self',
                    'uri' => '/books/'.$book->id,
                ]
            ],
        ];
    }

    /**
     * Include Author
     *
     * @return League\Fractal\ItemResource
     */
    public function includeAuthor(Book $book)
    {
        $author = $book->author;

        return $this->item($author, new AuthorTransformer);
    }
}

Embedding (including) stuff might be a bit more than you need right now, but it can be very handy too.



回答5:

In addition to @deczo's answer - I feel the $hidden variable is not really designed to be used dynamically. It is more to protect specific data from ever been incorrectly displayed (such as 'password').

If you want specific columns - you should probably just be using a select statement and just get the specific columns you want.



回答6:

In 5.4 you can hide and show attributes dinamically:

$model->makeVisible('attribute');

$model->makeHidden('attribute');

Laravel docs



回答7:

For Laravel 5.3 or greater version,

If you want to make multiple attributes temporary hidden or visible using single statement, you may use model->makeVisible() and model->makeHidden() methods with passing array of attributes.

For example, to hide multiple attributes,

$user->makeHidden(["attribute1", "attribute2", "attribute3"]);

And to make visible multiple attributes,

$user->makeVisible(["otherAttribute1", "otherAttribute2", "otherAttribute3"]);


回答8:

Made a package for this that uses Model Policies.

https://github.com/salomoni/authorized-attributes


Use the Salomoni\AuthorizedAttributes trait

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Salomoni\AuthorizedAttributes;

class Post extends Model
{
    use AuthorizedAttributes;

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array
     */
    protected $hidden = ['author_comments'];
}

Create and register a model policy. Add methods for the hidden attributes in camel-case prefixed with see.

namespace App\Policies;

use App\User;

class PostPolicy
{
    /**
     * Determine if a post author_comments-atrribute can be seen by the user.
     *
     * @param  \App\User  $user
     * @return bool
     */
    public function seeAuthorComments(User $user)
    {
        return $user->isAuthor();
    }
}