使用Eloquent的ORM将usermeta.id插入posts.usermeta_id

时间:2018-04-02 21:00:06

标签: php laravel laravel-5 eloquent slim

我正在开发一个用户生成的内容博客,该博客允许用户在提示注册之前完成整个上传过程。基本流程:填写表单以选择用户名/基本信息 - >上传博客帖子>提示用电子邮件/密码注册。扭转正常流量的目的是提高用户体验和转换率,并在开始时避免堵塞。

我没有迁移,而是在PHPmyAdmin中手动创建表。我有3个关系模型:Usermeta-> hasOne(App \ Mopdels \ Post),Post-> belongsTo(App \ Models \ Usermeta),User-> belongsTo(App \ Models \ Usermeta)。

我遇到的问题是,一旦用户创建了用户名并将第一个表单提交到usermeta表,然后提交第二个表单以将他们的博客帖子上传到帖子表,它就没有&#39 ; t似乎将usermeta.id附加到posts.usermeta_id将它们链接在一起。我必须遗漏一些东西或者没有正确地附上它。这是我的StoryController:

<?php

namespace App\Controllers\Story;

use App\Models\Post;
use App\Models\User;
use App\Models\Usermeta;
use App\Controllers\Controller;
use Respect\Validation\Validator as v;


class StoryUploadController extends Controller
{

  public function guidance($request, $response)
  {
    return $this->view->render($response, 'storyupload/guidance.twig');
  }

  //set up our the Upload Story class so the user can upload their story
  //render the view 'uploadstory.twig'
  public function getStoryUpload($request, $response)
  {
    return $this->view->render($response, 'storyupload/upload.twig');
  }

  // This method is called when the user submits the final form
  public function postStoryUpload($request, $response, $id)
  {
    //set up our validation rules for our complete sign up form
    $validation = $this->validator->validate($request, [
      'title' => v::stringType()->notEmpty()->length(1, 80),
      'body' => v::stringType()->notEmpty()->length(1, 2500),
    ]);

    //if validation fails, stay on story upload page
    if ($validation->failed()) {
      return $response->withRedirect($this->router>pathFor('storyupload.upload'));
    }

    $user = Usermeta::find($id)->first();

    //We can use our Post Model to send the form data to the database
    $post = Post::create([
      'title' => $request->getParam('title'),
      'body' => $request->getParam('body'),
      'category' => $request->getParam('category'),
      'files' => $request->getParam('img_path'),
      'usermeta_id' => usermeta()->attach($user->id),
    ]);

    //after submit, redirect to completesignup page
    return $response->withRedirect($this->router->pathFor('auth.completesignup'));
  }
}

我继续收到错误&#39; usermeta_id不能为空&#39;所以它绝对不能正确地从usermeta表中提取id。 我已经使用create()方法将usermeta数据发送到我的Auth控制器中的表。

在Auth控制器中提交所有表单提交是否更好?使用我的示例确保我的posts.usermeta_id链接到我的usermeta.id的正确方法是什么?

usermeta表单由我的Auth Controller负责:

//render the view 'signup.twig'
  public function getSignUp($request, $response)
  {
    return $this->view->render($response, 'auth/signup.twig');
  }

  // This method is called when the user submits the form
  public function postSignUp($request, $response)
  {
    $validation = $this->validator->validate($request, [
      'name' => v::notEmpty()->alpha(),
      'username' => v::noWhitespace()->notEmpty()->UsernameAvailable(),
      'city' => v::notEmpty()->alpha(),
      'country' => v::notEmpty()->alpha(),
    ]);

    //if validation fails, stay on signup page
    if ($validation->failed()) {
      return $response->withRedirect($this->router->pathFor('auth.signup'));
    }

    $usermeta = Usermeta::create([
      'name' => $request->getParam('name'),
      'username' => $request->getParam('username'),
      'city' => $request->getParam('city'),
      'country' => $request->getParam('country'),
      'share_location' => $request->getParam('share_location'),
    ]);

    //after submit, redirect to storyupload/guidance
    return $response->withRedirect($this->router>pathFor('storyupload.guidance'));
  }

1 个答案:

答案 0 :(得分:1)

我在这里写了很多。要直接跳到我认为可以解决您的问题,请参阅&#34;您的问题&#34;部分。剩下的就是教育活动。

Laravel关系快速介绍

正如您可能已经知道的那样,&#34;关系&#34;在Laravel中是从数据库中的硬数据派生的虚拟概念。因为它们是虚拟的,所以关系的定义有一些重叠。

当你说&#34; Usermeta有一个帖子&#34; - 这意味着posts表将有一个usermeta_id字段。

当你说&#34; Post属于Usermeta&#34; - 这意味着posts表将有一个usermeta_id字段。

请注意,这两个关系映射到完全相同的表中的完全相同的字段。声明一个关系将通过简单的同余来声明另一个关系。 &#34; Usermeta有一个帖子&#34;和&#34; Post属于Usermeta&#34;是相同的关系。

对你们关系的调整

另一个共享同一模式的关系(posts表有一个usermeta_id字段)。那就是&#34; Usermeta 有很多帖子&#34;。这里的区别不在于如何将关系存储到数据库中,而在于Laravel如何解释关系以及Laravel将运行的查询。

当您说&#34; Usermeta有一个发布&#34;时,Laravel将扫描数据库以查找匹配usermeta_id第一个帖子,将其作为Usermeta模型的一个实例返回。

当您说&#34; Usermeta有很多帖子&#34;时,Laravel将扫描数据库以查找匹配usermeta_id所有并将其作为Usermeta模型的集合。您可能想要这第二种行为 - 否则用户在注册后无法再发帖。

设置usermeta_id字段

Laravel允许您通过关系直接设置数据库字段。 See their documentation on inserting related models for details

由于许多关系只是同一底层架构的密码,因此无需双向插入或更新相关模型。例如,假设我们有以下两个模型:

class User extends Eloquent {
    public function posts() {
        return $this->hasMany("App\Post");
    }
}
class Post extends Eloquent {
    public function user() {
        return $this->belongsTo("App\User");
    }
}

在这种情况下,以下两行代码是相同的,您只需要使用其中一行:

$post->user()->associate($user);
$user->posts()->save($post);

这两个效果都相同(在user_id表上设置posts字段)

我提到这一点的原因是,您似乎正在尝试重复使用代码。您正在使用attach()(可以想象设置usermeta_id)并且您还要直接设置usermeta_id。我已在下面的attach方法中添加了附注 - 因为我不相信它是正确的方法。

要使用Laravel的关系,您需要以下代码来设置此字段:

  public function postStoryUpload($request, $response, $id)
  {
    //set up our validation rules for our complete sign up form
    $validation = $this->validator->validate($request, [
      'title' => v::stringType()->notEmpty()->length(1, 80),
      'body' => v::stringType()->notEmpty()->length(1, 2500),
    ]);

    //if validation fails, stay on story upload page
    if ($validation->failed()) {
      return $response->withRedirect($this->router>pathFor('storyupload.upload'));
    }

    $user = Usermeta::find($id)->first();

    //We can use our Post Model to send the form data to the database
    $post = Post::create([
      'title' => $request->getParam('title'),
      'body' => $request->getParam('body'),
      'category' => $request->getParam('category'),
      'files' => $request->getParam('img_path'),
    ]);

    // Set the usermeta_id field
    $post->usermeta()->associate($user);
    // Save the model so we write changes to the database
    $post->save();

    //after submit, redirect to completesignup page
    return $response->withRedirect($this->router->pathFor('auth.completesignup'));
  }

手动设置usermeta_id字段

您可以手动设置字段,而不是使用Laravel关系来设置此字段。这有时可能更干净,但它不那么明确,如果你不小心,可能会导致小错误。为此,您需要将usermeta_id字段视为模型上的任何其他字段。

$post->usermeta_id = $user->id;

当使用fillcreate批量分配属性时,这也适用:

$post = \App\Post::create([
    'title' => $title,
    'body' => $body,
    'usermeta_id' => $user->id
]);
$post->fill([
    'title' => $title,
    'body' => $body,
    'usermeta_id' => $user->id
]);

请注意,手动设置usermeta_id时,需要使用任何关系方法。以下代码是多余的:

$post->usermeta_id = $user->id;
$post->usermeta()->associate($user);

你的问题(我相信)

但是,对于大规模任务有一个警告。 Per the Laravel documentation,群发作业要求您填写模型的fillableguarded属性。

这是任何Laravel代码中最常见的错误之一,如果不是常见错误 - 并且它不会引发明显的错误,因此很容易小姐。考虑以下模型:

class Post extends Eloquent {
    private $fillable = ["title", "body"];
}

如果您尝试批量分配usermeta_id字段,请执行以下操作:

$post = \App\Post::create([
    'title' => $title,
    'body' => $body,
    'usermeta_id' => $user->id
]);

然后默默地失败。不会抛出任何错误,Post已创建,但usermeta_id字段将为NULL - 因为它不是可分配的。这可以通过更新您的模型来解决:

class Post extends Eloquent {
    private $fillable = ["title", "body", "usermeta_id"];
}

我将再次重复,如上所述,如果使用这样的质量分配,则需要使用associatesave关系方法。这将是多余的。因此,您只需将usermeta_id直接设置为$user->id,而无需任何usermeta()->associate()诡计。

我提到的错误

我提到手动设置这样的字段会导致错误。因此,让我们现在讨论一下这些错误,而不是掩饰它们。

如果您手动更新关系字段,Laravel将不会意识到这两个模型是相关的,直到它从数据库重新加载模型。考虑以下两个代码块:

$post = new Post();
$post->usermeta_id = $user->id;
dd( $post->usermeta->name );

$post = new Post();
$post->usermeta()->associate($user);
dd( $post->usermeta->name );

第一个代码块将失败,抛出错误&#34;无法读取null对象的属性&#34; - 因为就Laravel所知,$post->usermetaNULL。您设置$post->usermeta_id,但未设置$post->usermeta

第二个代码块将按预期工作,因为通过运行associate函数,它会同时设置usermeta_idusermeta

然而,95%的时间并没有真正引起任何问题。如果您正在使用异步API调用来保存帖子,然后使用单独的异步API调用来稍后阅读帖子,那么Laravel将从数据库中读取帖子并正确设置当我们看到填写usermeta_id字段时自动启动关系。

关于attach()方法

的旁注

Laravel使用不同的方法来保存不同类型的关系 - 因为不同的关系意味着不同的底层数据库字段。

  • associate:这会在当前模型表中设置*_id字段。例如:$post->user()->associate($user)会在user_id
  • 上设置posts
  • save:这会在其他模型表中设置*_id字段。例如:$post->comments()->save($comment)会在post_id
  • 上设置comments
  • attach:这会在many to many relationships的链接表上设置*_id个字段。例如,如果您有标记系统,那么$post->tags()->attach($tag)会在post_id表格上设置tag_idpost_tags

记住你需要的这三个功能中的哪一个可能有点棘手。一般来说,从关系到函数的直接映射是:

  • hasOne hasMany - &gt; 保存
  • belongsTo - &gt;的
  • belongsToMany - &gt;的