Php 使用雄辩的';s ORM将usermeta.id插入posts.usermeta\u id

Php 使用雄辩的';s ORM将usermeta.id插入posts.usermeta\u id,php,laravel,laravel-5,eloquent,slim,Php,Laravel,Laravel 5,Eloquent,Slim,我正在开发一个用户生成的内容博客,允许用户在被提示注册之前完成整个上传过程。基本流程:填写表格选择用户名/基本信息->上传博客帖子->提示使用电子邮件/密码注册。反转正常流量的目的是增加UX和转换率,并避免在开始时出现障碍 我没有迁移,而是在PHPmyAdmin中手动创建了表。我有3个关系模型:Usermeta->hasOne(App\Mopdels\Post)、Post->belongsTo(App\models\Usermeta)和User->belongsTo(App\models\Use

我正在开发一个用户生成的内容博客,允许用户在被提示注册之前完成整个上传过程。基本流程:填写表格选择用户名/基本信息->上传博客帖子->提示使用电子邮件/密码注册。反转正常流量的目的是增加UX和转换率,并避免在开始时出现障碍

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

我遇到的问题是,一旦用户创建了一个用户名并将第一个表单提交到usermeta表,然后提交第二个表单以将他们的博客文章上传到post表,它似乎就不会将usermeta.id附加到posts.usermeta_id,将它们链接在一起。我一定是漏了什么东西,或者没有正确连接。这是我的故事控制器:

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

我在这里写了不少。要直接跳到我认为可以解决您问题的内容,请参阅“您的问题”部分。其余的在这里作为一个教育练习

拉威尔关系简介 您可能已经知道,Laravel中的“关系”是从数据库中的硬数据派生的虚拟概念。因为它们是虚拟的,所以关系的定义有一些重叠

当您说“Usermeta有一篇文章”——这意味着
posts
表将有一个
Usermeta\u id
字段

当您说“Post属于Usermeta”时,这意味着
posts
表将有一个
Usermeta\u id
字段

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

对你们关系的调整 还有一个关系共享相同的模式(
posts
表有一个
usermeta\u id
字段)。也就是说“Usermeta有很多帖子”。这里的区别不在于关系如何存储到数据库,而在于Laravel如何解释关系以及Laravel将运行什么查询

当您说“Usermeta有一篇
Post”时,Laravel将扫描数据库,查找带有匹配的
Usermeta\u id的第一篇Post,并将其作为Usermeta模型的实例返回

当你说“Usermeta拥有许多帖子”时,Laravel将扫描数据库中所有匹配的
Usermeta\u id
s,并将它们作为Usermeta模型的集合返回。您可能需要第二种行为,否则用户在注册后将无法发布第二篇文章

设置
usermeta\u id
字段 Laravel允许您直接通过关系设置数据库字段

因为许多关系只是同一基础模式的密码,所以不需要双向插入或更新相关模型。例如,假设我们有以下两种模型:

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);
这两种方法都具有相同的效果(在
posts
表上设置
user\u id
字段)

我提到这一点的原因是,看起来您正试图在代码中实现二次探底。您使用的是
attach()
(可以想象是设置
usermeta\u id
),您还可以直接设置
usermeta\u 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\u id
字段 您可以手动设置该字段,而不是使用Laravel的关系来设置该字段。这有时可能更干净,但不太明确,如果不小心,可能会导致小错误。为此,您需要像对待模型上的任何其他字段一样对待
usermeta\u id
字段

$post->usermeta_id = $user->id;
这在使用
fill
create
批量分配属性时也有效,如下所示:

$post = \App\Post::create([
    'title' => $title,
    'body' => $body,
    'usermeta_id' => $user->id
]);
$post->fill([
    'title' => $title,
    'body' => $body,
    'usermeta_id' => $user->id
]);
请注意,当像这样手动设置
usermeta\u id
时,您不需要使用任何关系方法。以下代码是多余的:

$post->usermeta_id = $user->id;
$post->usermeta()->associate($user);
你的问题(我相信) 然而,大规模分配有一个警告,质量分配要求您填写模型的
可填充
受保护
属性

这是任何Laravel代码中最常见的错误之一,如果不是的话,也是最常见的错误之一,而且它不会抛出明显的错误,因此很容易出错。考虑下面的模型:

class Post extends Eloquent {
    private $fillable = ["title", "body"];
}
如果您尝试批量分配
usermeta\u id
字段,如下所示:

$post = \App\Post::create([
    'title' => $title,
    'body' => $body,
    'usermeta_id' => $user->id
]);
然后它将无声地失败。不会抛出错误,并且会创建
Post
,但是
usermeta\u id
字段将为
NULL
——因为它不可批量分配。通过如下方式更新模型可以解决此问题:

class Post extends Eloquent {
    private $fillable = ["title", "body", "usermeta_id"];
}
我将再次重复,正如我在上面所做的,如果像这样使用批量分配,您不需要使用
associate
save
关系方法。这是多余的。因此,您可以直接将
usermeta\u id
设置为
$user->id
,而无需任何
usermeta()->associate()

我提到的bug 我提到手动设置这样的字段可能会导致bug。让我们来讨论一下这些bug现在是什么,而不是掩饰它们

如果手动更新relationship字段,Laravel将不知道这两个模型是相关的,直到它从数据库重新加载模型
$post = new Post();
$post->usermeta_id = $user->id;
dd( $post->usermeta->name );

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