Eloquent模型上的自定义字段名称

时间:2017-04-20 16:48:02

标签: php laravel laravel-5 eloquent

我有一个表posts,此表包含一些列,例如pst_idpst_titlepst_contentpst_category_id等。我想在json输出上用一些更好的名字来表示这些字段,实际上我正在尝试从列名中删除前缀pst_

我测试了多种方法。起初我尝试在DB层上为这些列创建别名,例如Post::select(['pst_id as id'])->get()。这个想法通常很糟糕,因为它使得列名在整个软件中不一致(每个开发人员可能有一个命名字段的约定)。所以我坚持找到一种方法来命名模型层上的列。

下一个解决方案是使用AccessorsMutators。虽然它涵盖了以前的问题,但是每个模型实现20种方法真的很难! 20x100~2000方法!!! :/

我测试的最后一个解决方案是使用https://github.com/jarektkaczyk/eloquencemappable功能。这非常好,我可以将所有旧字段放到$hidden属性中,并将新字段添加到$appends以显示输出。但是这个解决方案也存在问题。如果我将所有新字段添加到$appends,当我使用select语句选择某些列时,未选择的列将在输出中显示null值:|。好吧,我试图在基础模型上覆盖mappedselectparseMappings方法,以便动态地向$appends添加新名称,但它并不能让我满意。事实上,它在使用时变得非常棘手,我不确定团队是否可以接受并轻松使用它。

所以这就是问题所在:“有没有办法在输出中重命名列的名称以进行雄辩?”。 GoLang有一个非常好的功能,称为结构标签。您可以为结构定义一些标记,例如:

type Post struct {
  Pst_id            int       `json:"id"`
  Pst_title         string    `json:"title"`
  Pst_content       string    `json:"content"`
}

当你使用Post生成一个json.Marshal结构的json时,根据标签,它会给你一个像这样的json:

{
  "id": 23,
  "title": "Custom Field Tags for Eloquent",
  "content": "I tried a lot of things, but they are hard. I'm a programmer so I'm lazy! What can I do?",
}

我认为我们在php世界中没有这样的东西,但有没有办法使用后面的doctrine's annotation概念来实现Go中的标记结构?

欢迎任何评论和想法!

2 个答案:

答案 0 :(得分:3)

第一步是在这些模型上覆盖几个方法。第一种方法是getAttribute(),当您访问模型的属性时可以调用它,以便您可以访问它。您希望能够在没有pst_前缀的情况下访问该属性,以便执行以下操作:

public function getAttribute($key)
{
    if(array_key_exists($prefixedKey = 'pst_'.$key, $this->attributes)) {
        return $this->attributes[$prefixedKey];
    }

    return parent::getAttribute($key);
}

然后为了确保在转换为json时键不具有前缀,您将覆盖输出json时调用的attributesToArray()方法,并且还会尊重您的$hidden$visible$casts$dates数组。那将是这样的:

public function attributesToArray()
{
    $attributes = parent::attributesToArray();

    $mutated = [];
    foreach ($attributes as $key => $value) {
        $mutated[preg_replace('/^pst_/', '', $key)] = $value;
    }

    return $mutated;
}

要实现这些,您可以使用实现这些方法的抽象类扩展Model类,并让您的类扩展该基类或使用这些方法创建特征,并让您的类实现该特征。

答案 1 :(得分:2)

我可能会使用联盟中的Fractal Transformer

您基本上创建了一个映射类并将其应用于集合。

Transformer类看起来像这样

<?php

namespace App;

use App\Post;
use League\Fractal;
use League\Fractal\TransformerAbstract;

class PostTransformer extends TransformerAbstract{

    public function transform(Post $post)
    {
        return [
            'id'     => (int) $post->pst_id,
            'title'  => $post->pst_name,
            'content'  => $post->pst_uuid,
        ];
    }
}

然后在您的控制器中或您转换集合的地方。

$posts = Post::all();
$manager = new Manager();
$manager->setSerializer(new CursorSerializer());
$resource = new Collection($posts, new PostTransformer());
$formattedCollection = $manager->createData($resource);

文档相当不错,实现它非常简单。