【问题标题】:Optimising code used for inserting, updating or deleting relationship优化用于插入、更新或删除关系的代码
【发布时间】:2016-10-06 11:38:13
【问题描述】:

我正在编写基于提供的输入和现有数据插入、更新或删除表行的逻辑。

  • 如果没有输入,并且该行存在,则应删除该行。
  • 如果有输入,但该行不存在,则应该创建它。
  • 最后,如果有输入且行存在,则应更新行。

该项目是attributes 表的测试代码,该表具有namevaluemedia_id(它引用已被赋予属性的媒体片段)。 namemedia_id 一起是唯一的(一个媒体不能有两个同名的属性)。使用插入、更新和删除对项目很重要,而不是如果为空则将它们留空。

我的问题是是否有更简单的方法来执行此逻辑。我可以只将它用于一个输入,但是当有两个(namedescription)并且我以后可能会添加更多时,它似乎需要使用大量代码。有没有更有效的方法?


我的数据库迁移:

Schema::create('attributes', function (Blueprint $table) {
    $table->increments('id')->unsigned();
    $table->integer('media_id')->unsigned();
    $table->string('name');
    $table->text('value')->nullable();
    $table->timestamps();
    $table->unique(['media_id', 'name']);
    $table->foreign('media_id')->references('id')->on('media')->onDelete('cascade');
});

我的看法:

<form action="{{ Request::path() }}" method="post">
    <input type="hidden" name="_token" value="{{ csrf_token() }}">
    <label style="display:block" for="name">Name</label>
    <input type="text" name="name" id="name" value="{{ $attributes->get('name') ? $attributes->get('name')->value : '' }}">
    <label style="display:block" for="description">Description</label>
    <textarea name="description" id="description">{{ $attributes->get('description') ? $attributes->get('description')->value : '' }}</textarea>
    <button type="submit">Save</button>
</form>

我的临时控制器:

Route::post('/', function(Request $request) {
    $media = Media::find(1);

    $attributes = $media->attributes->keyBy('name');

    if($request->input('name')) {
        if($attributes->get('name')) {
            $attributes->get('name')->value = $request->input('name');
            if(!$attributes->get('name')->save()) {
                return "error updating name";
            }
        } else {
            $attribute = new Attribute;
            $attribute->name = 'name';
            $attribute->value = $request->input('name');
            if(!$media->attributes()->save($attribute)) {
                return "error inserting name";
            }
        }
    } else {
        if($attributes->get('name')) {
            if(!$attributes->get('name')->delete()) {
                return "error deleting name";
            }
        }
    }
    if($request->input('description')) {
        if($attributes->get('description')) {
            $attributes->get('description')->value = $request->input('description');
            if(!$attributes->get('description')->save()) {
                return "error updating description";
            }
        } else {
            $attribute = new Attribute;
            $attribute->name = 'description';
            $attribute->value = $request->input('description');
            if(!$media->attributes()->save($attribute)) {
                return "error inserting description";
            }
        }
    } else {
        if($attributes->get('description')) {
            if(!$attributes->get('description')->delete()) {
                return "error deleting description";
            }
        }
    }
});
return redirect('/');

(还有一个用于 GET 的控制器,它显示表单和数据库输出。我没有打扰它,因为我觉得没有必要。)


我死的简单模型:

class Attribute extends Model
{
    public function media()
    {
        return $this->belongsTo('App\Media');
    }
}

【问题讨论】:

  • 有几件事,if($request-&gt;input('name')) 部分应该是 if($request-&gt;has('name')),这将始终返回 truefalse。如果它返回false,您可以在桌子上运行delete。您可以使用attribute 表上的firstOrNew 方法通过搜索modelname 来更新或插入attribute 到表中。
  • 为什么不使用 restful 路由和内置验证?
  • @KevinCompton 它正在以特定方式用于用户界面,但我正在考虑根据此处的信息实际重写它,然后使用 Angular 使用户界面运行。
  • @Furze Ahhh 我明白了。我推荐 Vuejs,比 Angular 干净得多。
  • @Furze,我使用 Angular 作为我所有项目的前端,并使用 Laravel 作为 API 框架。特别适用于速率限制和可用的 oAuth 包。

标签: php mysql laravel model eloquent


【解决方案1】:

好的,这就是我得到的:

路线文件:

use Illuminate\Http\Request;
use App\Models\Media;
use App\Models\Attribute;

Route::get('/', function(Request $request) {
    $media = Media::all();
    $media->attributes;
    return response()->json($media);
});
Route::get('/media/{id}', function($id, Request $request) {
    $media = Media::find($id);
    $media->attributes;
    return response()->json($media);
});
Route::post('/media/{id}', function($id, Request $request) {
    if($request->has('name')) {
        $attr = Attribute::firstOrNew(['name' => 'name', "media_id" => $id]);
        $attr->value = $request->input('name');
        $attr->save();
    } else {
        Attribute::where('name', 'name')->where("media_id",$id)->first()->delete();
    }
    if($request->has('description')) {
        $attr = Attribute::firstOrNew(['name' => 'description', "media_id" => $id]);
        $attr->value = $request->input('description');
        $attr->save();
    } else {
        Attribute::where('name', 'description')->where("media_id",$id)->first()->delete();
    }
});
return redirect('/');

媒体模型:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Media extends Model
{
    protected $table = 'media';

    protected $appends = [];

    public function attributes() {
        return $this->hasMany(Attribute::class);
    }
}

属性模型

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Attribute extends Model
{
    protected $table = 'attributes';

    protected $fillable = ['name', 'media_id'];

    public function media() {
        return $this->belongsTo(Media::class);
    }
}

基本上,通过将 Attributes 设为模型,我能够在属性表上执行 firstOrNew(),并在 firstOrNew() 上查找 ['name' =&gt; 'name', "media_id" =&gt; $id] 使其要么找到匹配的行,要么创建一个新行.然后,您可以根据这些更新值列。

更短,更简化,它可以让你的属性成为一个模型!

(我认为,几乎所有东西都应该是模型:P)

编辑:我将其修改为我最近的代码。将其从 belongsTo("Media") 更改为 belongsTo(Media::class),并从路由文件中删除了 dd()

【讨论】:

  • 谢谢!请问,$media-&gt;attributes; 本身有什么意义?
  • @Furze 我假设他只是想加载attributes 属性,所以它将包含在响应中。您可以通过预先加载 attributes 来做到这一点,不过:Media::with('attributes')-&gt;get();'/' 路由中,Media::with('attributes')-&gt;find($id);'/media/{id}' 路由中。
  • 我本可以使用with,但我选择了手动方式。只是个人喜好而已。
猜你喜欢
  • 1970-01-01
  • 2011-10-25
  • 2012-03-23
  • 2020-03-18
  • 2017-07-05
  • 1970-01-01
  • 2013-07-03
  • 2013-09-22
  • 2014-12-28
相关资源
最近更新 更多