如何避免因雄辩的模型而使模型膨胀? [英] How to avoid model bloat with eloquent models?

查看:94
本文介绍了如何避免因雄辩的模型而使模型膨胀?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在业余时间尝试在Laravel中做一个小游戏,但我一直在如何最好地构建应用程序以及某些模型变得肿的问题上遇到麻烦.

I've been trying to make a small game in Laravel in my spare time and I've been running into trouble with how best to structure the application and issues with certain models getting very bloated.

我目前正在使用雄辩的模型并将功能直接附加到它们上.因此,例如,我有一个User模型,该模型以以下功能开始

I'm currently using eloquent models and attaching functionality directly to them. So for example, I have a User model, which started out with the below functions

$user->verify()
$user->creditGold()
$user->debitGold()

这似乎是合理的.当我向网站添加功能时,该类开始变得越来越笨拙,例如:

which seemed reasonable. As I added functionality to the site, the class started getting bigger and unwieldy though, for example:

$user->creditItem()
$user->debitItem()
$user->equipItem()
$user->unequipItem()
$user->moveToTown()
$user->takeQuest()
$user->ban()
$user->unban()
// etc. etc. 

有很多感觉很不相关的代码已被塞入这一类,而且非常混乱.

there's a lot of code that feels very unrelated that's been shoved into this one class and it's very messy.

我已经开始做的工作是创建由User类实例化并保存的助手模型.下面的例子

What I've started doing is making helper models that are instantiated and held by the User class. example below

$user->inventory()->creditItem()
$user->inventory()->debitItem()
$user->inventory()->useItem()

易于调用和使用,但感觉不正确.

It's easy to call and work with but it feels incorrect.

有人对如何最好地分解在概念上都属于同一实体的大量代码有任何建议吗?我喜欢功能与数据结合的想法,因为我认为这是最自然的方法了解OO,但是对我来说,将代码抽象到Service层并具有将用户作为参数并对其执行操作的服务类(即$service->giveItemToUser($user, $item)),对我来说会更好吗?

Does anyone have advice for how to best break down a large mass of code that all conceptually belongs to the same entity? I like the idea of functionality being coupled with data because I think that's the most natural way of understanding OO, but would it be better for me to abstract the code out to a Service layer and have service classes that take the user as a parameter and acts on it instead (i.e. $service->giveItemToUser($user, $item) )?

推荐答案

这是SoC(关注分离)原理变得非常重要的地方.这意味着确保您的应用程序的每个部分仅与需要关注的内容有关.

This is where the principle of SoC (Separation of Concerns) becomes very important. What this means is making sure each piece of your app is only concerned with what it needs to be concerned with.

让我们从确定User类中的一些问题开始.

Lets start by identifying some of the concerns in your User class.

  1. 库存
  2. 设备
  3. 任务

以上是您的用户将使用的常规资源.其中每一个也都涉及到他们关心的事情:

The above are the general resources that will be utilized by your user. Each of these also have things they are concerned with:

  1. 库存
    • 项目
  1. Inventory
    • Item
  • 项目
  • 任务

您已经可以看到我们有几个需要相同信息的用户部分.

You can already see we have several separate parts of the user which require the same information.

在此阶段,我们现在需要分离其他一些问题.具体来说,业务逻辑(我们要对数据进行的处理)和数据访问层本身(ORM/模型).就个人而言,我喜欢使用存储库模式将这些内容分开.在模型上工作并与整体逻辑和应用程序有关的类.我认为模型是状态的一种表示,应该只担心获取或保留该状态.

At this stage, we now need to separate some other concerns. Specifically, the business logic (what we want to do with our data) and the data access layer itself (the ORM/Models). Personally, I like to keep these things separate by using the Repository Pattern. Classes that work on models and are concerned with the overall logic and application process. I feel that models are a representation of state, and should only worry about fetching or persisting that state.

所以我将这些东西分解为这样:

So I split these things out as such:

  1. 模型

  1. Models

  • 用户
  • 项目
  • 任务

存储库(依赖项)

  • UserRepository(用户,项目,库存,设备,任务)
  • 库存仓库(项目)
  • EquipmentRepository(项目,集合)
  • QuestRepository(任务)

代码示例

现在,这为我提供了所需设置和组织的清晰定义.但是让我们给出一些示例代码.这与数据的持久性(手动或通过雄辩的关系等)无关.

Code Examples

Now this gives me a clear definition of the setup and organization I want. But lets give some example code. This does not concern how the data is persisted (either manually, or via Eloquent relationships, etc).

<?php namespace App\Repositories;

use App\Models\Item;
use Illuminate\Support\Collection;

class Inventory {

    protected $contents;

    public function __construct(Item $item, Collection $contents)
    {
        $this->item = $item;
        $this->contents = $contents;
    }

    public function add(Item $item)
    {
        $this->contents->push($item);
    }

    public function remove(Item $item)
    {
        $this->contents->forget($item->id);
    }

    public function contains(Item $item)
    {
        return $this->contents->has($item->id);
    }
}

InventoryRepository仅与管理其项目集合有关.添加它们,删除它们,然后检查是否还有其他项.为此,取决于Collection类和Item模型.

The InventoryRepository is only concerned with managing its collection of items. Adding them, removing them and checking if other items are there. To do this it depends on the Collection class and the Item model.

<?php namespace App\Repositories;

use App\Models\Item;

class Equipment {

    protected $slots = [
        'head' => null,
        'body' => null,
        'legs' => null,
        'feet' => null,
        'arms' => null,
    ];

    public function __construct(Item $item)
    {
        $this->item = $item;
    }

    public function get($slot)
    {
        return $this->slots[$slot];
    }

    public function set($slot, Item $item)
    {
        $this->slots[$slot] = $item;
    }

    public function empty($slot)
    {
        $this->slots[$slot] = null;
    }

    public function hasEquipment($slot)
    {
        return !empty($this->get($slot));
    }

    public function isEquipped(Item $item) 
    {
        if ($this->hasEquipment($item->slot))
        {
            return $this->get($item->slot)->id == $item->id;
        }
    }
}

另一类仅涉及当前配备的物品.配备,取消配备等

Another class only concerned with the items currently equipped. Equipping, unequipping, etc.

一旦定义了单独的片段,就可以将它们全部放入UserRepository类中.通过将它们作为依赖项拉入,UserRepository中包含的代码将明确基于用户管理,而访问已加载的依赖项将为您提供所需的所有功能.

Once you've defined your separate pieces, you can then bring them all into your UserRepository class. By pulling them in as dependencies, the code contained within your UserRepository will be explicitly User management based, while accessing the loaded dependencies gives you all the functionality you require.

<?php App\Repositories;

use App\Models\User;
use App\Repositories\Inventory;
use App\Repositories\Equipment;

class User {

    protected $user;
    protected $quests;
    protected $equipment;
    protected $inventory;

    public function __construct(
        User $user,
        Quests $quests,
        Equipment $equipment,
        Inventory $inventory
    ) {
        $this->user = $user;
        $this->quests = $quests;
        $this->equipment = $equipment;
        $this->inventory = $inventory;
    }

    public function equip(Item $item)
    {
        if ($this->inventory->contains($item))
        {
            $this->equipment->set($item->slot, $item);
        }
    }

    public function unequip(Item $item)
    {
        if ($this->equipment->isEquipped($item)) 
        {
            $this->equipment->empty($item->slot);
        }
    }
}

这又仅仅是组织代码的概念.在这种类型的设置中,您希望如何将数据加载和持久存储到数据库中.这也不是组织代码的唯一方法.这里的要点是如何将您的代码分成单独的部分和关注点,以更好地模块化和隔离功能,使其更易于管理和摘要.

This is again just a concept for organizing code. How you want to load and persist the data to the DB is up to you within this type of setup. This is also not the only way to organize the code. The takeaway here is how to break your code out into separate parts and concerns to better modularize and isolate functionality into easier to manage and digest bits.

我希望这会有所帮助,不要犹豫,问任何问题.

I hope this was helpful, don't hesitate to ask any questions.

这篇关于如何避免因雄辩的模型而使模型膨胀?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆