【问题标题】:Dynamically hide certain columns when returning an Eloquent object as JSON?将 Eloquent 对象返回为 JSON 时动态隐藏某些列?
【发布时间】:2014-09-05 16:08:03
【问题描述】:

当将 Eloquent 对象返回为 JSON 时,如何动态隐藏某些列?例如。隐藏“密码”列:

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

我知道我可以在模型中设置受保护的属性($hidden$visible),但是如何动态设置这些属性?我可能想在不同的上下文中隐藏或显示不同的列。

【问题讨论】:

    标签: laravel laravel-4 eloquent


    【解决方案1】:
    $model->getHidden();
    $model->setHidden(array $columns);
    
    $model->setVisible(array $columns);
    

    【讨论】:

    • 如果我setHidden('something')。属性somethingis 是否将未来表示形式移除为 JSON?我应该把原来的隐藏数组放回去吗?
    • @hugomosh 它在实例范围内工作,因此 something 将在调用实例时隐藏,除非您再次设置它。但不会影响其他机型。
    【解决方案2】:

    除了@deczo 的回答——我觉得$hidden 变量并不是真正设计为动态使用的。更多的是保护特定数据不被错误显示(例如“密码”)。

    如果您想要特定的列 - 您可能应该只使用 select 语句并获取您想要的特定列。

    【讨论】:

    • 这是为什么呢? visible/hidden 字段应该完全像这样工作 - 隐藏或不隐藏我们不想包含在 toArray/toJson 输出中的属性。当然不是唯一的方法。
    • 好吧-出于与“选择”存在相同的原因-获取您想要显示/不显示的列。我不是说你不应该这样做 - 但我不觉得它是设计的方式。即我认为您的答案是正确的 - 这只是另一种想法。
    • 只是询问您的意见,不用担心。当您使用 ORM 模型时,我觉得使用 select 很奇怪,这就是原因。
    • @The Shift Exchange 如果我们将问题扩展到预加载(在相关模型中动态隐藏列),您会选择两种技术中的哪一种?
    【解决方案3】:

    我不认为 ORM 的工作是担心表示逻辑,这就是 JSON。您将需要将数据转换为各种类型以及隐藏事物,有时还需要创建一个缓冲区来安全地重命名事物。

    您可以使用 Fractal 来完成所有这些工作,我正是出于这个原因而构建的。

    <?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);
        }
    }
    

    嵌入(包括)内容可能比您现在需要的多一点,但它也可以非常方便。

    【讨论】:

    • 您将如何根据每个请求添加/删除字段,例如
    • /book/22//chapters?fields[]=words&amp;fields[]=color
    • 与手动循环和创建 json 结构相比,使用 Fractal 的优势是什么,除了看起来更简洁的代码?
    • @Fahmi 嘿!我是 Fractal 的原作者。这是非常基础的,这是一个结构化的、可重用的 array_map,在一个你可以藏起来的类中完成。当您进入分页/光标和包含/关系逻辑时,它会获得更大的力量。
    • @PhilSturgeon 我实际上已经在我的最新项目中实现了它,我似乎很喜欢它。干得好:)
    【解决方案4】:

    我找到了一个完整的解决方案,使用 $model->setHidden(array $columns);

    例如,假设您想在控制器中准确决定要返回哪些字段。例如,仅更新模型的隐藏会迫使您在返回模型数组之前检查每个模型。当这些模型具有您也想更改的关系时,问题会变得更糟。您必须遍历每个模型,设置隐藏属性,然后为每个模型设置隐藏的关系。真是一团糟。

    我的解决方案包括为每个模型创建一个静态成员,当它存在时,在调用“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();
        }
    
    }
    

    作为额外的奖励,我公开了一种方法来显示您可能在模型的类中设置的模型的默认隐藏/可见。

    别忘了添加特质

    class Client extends Eloquent {
         use DynamicHiddenVisible;
    }
    

    最后,在控制器中,在返回模型之前,决定可见/隐藏属性:

    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();
    }
    

    【讨论】:

    • 谢谢你!真棒答案!如果您将 if 保护更改为使用 if (is_array(self::getStaticHidden())) 中的 is_array,您将能够将一个空数组传递给 setter 方法并一次隐藏/显示所有字段。
    • 这个很棒的解决方案启发了我将它扩大一点,我做了这个gist,您可以在其中动态设置模型的更多属性,而不仅仅是“隐藏”或“可见”。干杯。
    【解决方案5】:

    来自Lavarel 5.3 Documentation

    临时修改属性可见性

    如果您想让一些通常隐藏的属性在给定模型实例上可见,您可以使用makeVisible 方法。 makeVisible 方法返回模型实例以方便方法链接:

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

    同样,如果您想在给定模型实例上隐藏一些通常可见的属性,您可以使用makeHidden 方法。

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

    【讨论】:

    • 所以如果有人提出这个问题,它不会被弃用。
    【解决方案6】:

    对于 Laravel 5.3 或更高版本

    如果您想使用单个语句使多个属性临时隐藏或可见,您可以使用 model-&gt;makeVisible()model-&gt;makeHidden() 方法并传递 array of attributes

    例如,隐藏多个属性

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

    为了使多个属性可见

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

    【讨论】:

      【解决方案7】:

      在 5.4 中,您可以动态地隐藏和显示属性:

      $model->makeVisible('attribute');
      
      $model->makeHidden('attribute');
      

      Laravel docs

      【讨论】:

        【解决方案8】:

        为此制作了一个使用模型策略的包。

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


        使用Salomoni\AuthorizedAttributes 特征

        <?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。添加以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();
            }
        }
        

        【讨论】:

        • 这个支持隐藏特定的关系字段吗?
        【解决方案9】:

        在模型中:

        protected $hidden = [
            'your_field_1',
            'your_field_2',
        ];
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-06-13
          • 2014-09-23
          • 1970-01-01
          • 2015-04-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-04-18
          相关资源
          最近更新 更多