在 Laravel 5.3 中创建相关模型实例时出错
Error creating related model instances in Laravel 5.3
我刚刚开始一个基于 Laravel 的项目,我在创建与另一个相关的模型实例时遇到问题。总之,我有一个 "Company" 模型 class 引用了一个 "companies" MySQL table,还有一个 "Location" 模型 class引用 "locations" table。两个 table 都是相关的(一家公司有很多地点,每个地点都属于一家公司)。到目前为止一切顺利。
我有一个 "Middleware" 机制检查至少一个公司的存在,如果没有公司,我假设这是第一次 运行ning 系统,所以我显示"Create Company" controller/action 这样用户就创建了第一家公司。在提交时,这也会使用完全相同的公司信息创建一个位置记录,所以最后数据库中的位置记录应该有 "company_id" 刚刚创建的公司记录的 ID,但这并没有发生.
让我显示与此问题相关的现有文件和 classes:
创建公司的迁移文件table:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateCompaniesTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up() {
Schema::create('companies', function (Blueprint $table) {
$table->increments('id');
$table->string('nit');
$table->string('name');
$table->string('contact_name')->nullable();
$table->string('address')->nullable();
$table->string('phone')->nullable();
$table->string('email')->nullable();
$table->string('website')->nullable();
$table->timestamps();
$table->softDeletes();
$table->integer('created_by')->unsigned()->nullable();
$table->integer('updated_by')->unsigned()->nullable();
$table->integer('deleted_by')->unsigned()->nullable();
$table->foreign('created_by')->references('id')->on('users')
->onDelete('cascade');
$table->foreign('updated_by')->references('id')->on('users')
->onDelete('cascade');
$table->foreign('deleted_by')->references('id')->on('users')
->onDelete('cascade');
$table->index('nit');
$table->index('name');
$table->index('created_at');
$table->index('deleted_at');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down() {
Schema::dropIfExists('companies');
}
}
创建位置的迁移文件table:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateLocationsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up() {
Schema::create('locations', function (Blueprint $table) {
$table->increments('id');
$table->integer('company_id')->unsigned()->nullable();
$table->string('nit');
$table->string('name');
$table->string('contact_name')->nullable();
$table->string('address')->nullable();
$table->string('phone')->nullable();
$table->string('email')->nullable();
$table->string('website')->nullable();
$table->timestamps();
$table->softDeletes();
$table->integer('created_by')->unsigned()->nullable();
$table->integer('updated_by')->unsigned()->nullable();
$table->integer('deleted_by')->unsigned()->nullable();
$table->foreign('created_by')->references('id')->on('users')
->onDelete('cascade');
$table->foreign('updated_by')->references('id')->on('users')
->onDelete('cascade');
$table->foreign('deleted_by')->references('id')->on('users')
->onDelete('cascade');
$table->foreign('company_id')->references('id')->on('companies')
->onDelete('cascade');
$table->index('nit');
$table->index('name');
$table->index('created_at');
$table->index('deleted_at');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down() {
Schema::dropIfExists('locations');
}
}
公司型号class
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* App\Company
*/
class Company extends Model {
use SoftDeletes;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'nit', 'name', 'contact_name', 'address', 'phone', 'email', 'website',
];
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $dates = ['deleted_at'];
/**
* Get the users for the company.
*/
public function users() {
return $this->hasMany(User::class);
}
/**
* Get the locations for the company.
*/
public function locations() {
return $this->hasMany(Location::class);
}
/**
* Get the invoices for the company.
*/
public function invoices() {
return $this->hasMany(Invoice::class);
}
/**
* Get the user that created the record.
*/
public function createdBy() {
return $this->belongsTo(User::class, 'created_by');
}
/**
* Get the last user that updated the record.
*/
public function updatedBy() {
return $this->belongsTo(User::class, 'updated_by');
}
/**
* Get the user that removed the record.
*/
public function deletedBy() {
return $this->belongsTo(User::class, 'deleted_by');
}
/**
* Scope a query to only include the first active company.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function active($query) {
return $query->orderBy('id')->limit(1);
}
}
位置模型class:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* App\Location
*/
class Location extends Model {
use SoftDeletes;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'nit', 'name', 'contact_name', 'address', 'phone', 'email', 'website', 'company_id',
];
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $dates = ['deleted_at'];
/**
* Get the company that owns the location.
*/
public function company() {
return $this->belongsTo(Company::class);
}
/**
* Get the products for the location.
*/
public function products() {
return $this->hasMany(Product::class);
}
/**
* Get the inventory records for the location.
*/
public function inventories() {
return $this->hasMany(Inventory::class);
}
/**
* Get the user that created the record.
*/
public function createdBy() {
return $this->belongsTo(User::class, 'created_by');
}
/**
* Get the last user that updated the record.
*/
public function updatedBy() {
return $this->belongsTo(User::class, 'updated_by');
}
/**
* Get the user that removed the record.
*/
public function deletedBy() {
return $this->belongsTo(User::class, 'deleted_by');
}
}
提到的检测系统第一个运行的中间件:
<?php
namespace App\Http\Middleware;
use App\Company;
use Closure;
class CheckSystemFirstRun {
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next) {
/** @var \Illuminate\Http\Response $response */
$response = $next($request);
// The verification must be done AFTER the response has been generated, otherwise the request's route is
// unknown.
if ($request->route()->getName() != 'company.create') {
// Check if there are no active companies.
if (Company::count() == 0) {
return redirect(route('company.create'));
}
} else {
// Check if there are active companies.
if (Company::count() > 0) {
return redirect(route('dashboard'));
}
}
return $response;
}
}
允许用户输入第一个公司和位置记录的 CompanyController class:
<?php
namespace App\Http\Controllers;
use App\Company;
use App\Http\Requests\AddCompanyRequest;
use Illuminate\Http\Request;
class CompanyController extends Controller {
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create() {
return view('company.create');
}
/**
* Store a newly created resource in storage.
*
* @param AddCompanyRequest $request
* @param Company $company
* @return \Illuminate\Http\Response
*/
public function store(AddCompanyRequest $request, Company $company) {
$company->create($request->all());
// If there are no locations, create one using the same data as the received to create the company.
if ($company->locations->count() == 0) {
$company->locations()->create($request->all());
}
return redirect()->route('company.create');
}
}
还包含公司创建验证的指定请求 class:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class AddCompanyRequest extends FormRequest {
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() {
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules() {
return [
'nit' => 'required|max:255',
'name' => 'required|max:255',
'email' => 'required|email|unique:companies|max:255',
'website' => 'url|max:255',
];
}
}
当数据库是全新的并且我 运行 系统时,我被重定向到 "Create Company" 操作。提交后,新公司记录创建成功,但创建预期位置记录时与公司记录没有预期关系(company_id 外键列保持 NULL)。
我正在关注 the recommendation from Laravel 5.3,所以我不确定我的代码有什么问题。
在发布此问题之前,我发现位置 table 中的 company_id 字段可能需要在迁移中定义为可空;以前不是这样的,但那时 Laravel/PHP 响应完整性错误 MySQL 因为 "company_id" 字段不能为空。我还尝试 dd() 用于在新记录上定义 company_id 的参数,但返回 id 值的函数始终返回 null。另外,我试过:
$company->locations()->create($request->all());
和
$location = new Location($request->all());
$company->locations()->save($location);
都没有成功。
我正在使用 MySQL Ver 15.1 Distrib 10.1.10-MariaDB,适用于 Windows 10 x64,PHP 7.0.4.
上的 Win32 (AMD64)
非常感谢任何帮助。谢谢。
更新 01
这是执行操作时执行的查询的输出:
----------------------
Query: insert into `companies` (`nit`, `name`, `contact_name`, `address`, `phone`, `email`, `website`, `updated_at`, `created_at`) values (?, ?, ?, ?, ?, ?, ?, ?, ?)
Bindings: array (
0 => '1113332323-9',
1 => 'TEST COMPANY INC',
2 => 'JOHN DOE',
3 => '1362 36TH PL',
4 => '8889990099',
5 => 'test@test.com',
6 => 'http://test.com',
7 => '2017-01-16 00:16:25',
8 => '2017-01-16 00:16:25',
)
Time: 4.5099999999999998
----------------------
Query: select * from `locations` where `locations`.`company_id` is null and `locations`.`company_id` is not null and `locations`.`deleted_at` is null
Bindings: array (
)
Time: 0.48999999999999999
----------------------
Query: insert into `locations` (`nit`, `name`, `contact_name`, `address`, `phone`, `email`, `website`, `company_id`, `updated_at`, `created_at`) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Bindings: array (
0 => '1113332323-9',
1 => 'TEST COMPANY INC',
2 => 'JOHN DOE',
3 => '1362 36TH PL',
4 => '8889990099',
5 => 'test@test.com',
6 => 'http://test.com',
7 => NULL,
8 => '2017-01-16 00:16:25',
9 => '2017-01-16 00:16:25',
)
Time: 4.5300000000000002
您正在将位置添加到 Company
的 ORM 实例,而不是新创建的记录。
$company->create($request->all());
应该是
$company = $company->create($request->all());
我刚刚开始一个基于 Laravel 的项目,我在创建与另一个相关的模型实例时遇到问题。总之,我有一个 "Company" 模型 class 引用了一个 "companies" MySQL table,还有一个 "Location" 模型 class引用 "locations" table。两个 table 都是相关的(一家公司有很多地点,每个地点都属于一家公司)。到目前为止一切顺利。
我有一个 "Middleware" 机制检查至少一个公司的存在,如果没有公司,我假设这是第一次 运行ning 系统,所以我显示"Create Company" controller/action 这样用户就创建了第一家公司。在提交时,这也会使用完全相同的公司信息创建一个位置记录,所以最后数据库中的位置记录应该有 "company_id" 刚刚创建的公司记录的 ID,但这并没有发生.
让我显示与此问题相关的现有文件和 classes:
创建公司的迁移文件table:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateCompaniesTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up() {
Schema::create('companies', function (Blueprint $table) {
$table->increments('id');
$table->string('nit');
$table->string('name');
$table->string('contact_name')->nullable();
$table->string('address')->nullable();
$table->string('phone')->nullable();
$table->string('email')->nullable();
$table->string('website')->nullable();
$table->timestamps();
$table->softDeletes();
$table->integer('created_by')->unsigned()->nullable();
$table->integer('updated_by')->unsigned()->nullable();
$table->integer('deleted_by')->unsigned()->nullable();
$table->foreign('created_by')->references('id')->on('users')
->onDelete('cascade');
$table->foreign('updated_by')->references('id')->on('users')
->onDelete('cascade');
$table->foreign('deleted_by')->references('id')->on('users')
->onDelete('cascade');
$table->index('nit');
$table->index('name');
$table->index('created_at');
$table->index('deleted_at');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down() {
Schema::dropIfExists('companies');
}
}
创建位置的迁移文件table:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateLocationsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up() {
Schema::create('locations', function (Blueprint $table) {
$table->increments('id');
$table->integer('company_id')->unsigned()->nullable();
$table->string('nit');
$table->string('name');
$table->string('contact_name')->nullable();
$table->string('address')->nullable();
$table->string('phone')->nullable();
$table->string('email')->nullable();
$table->string('website')->nullable();
$table->timestamps();
$table->softDeletes();
$table->integer('created_by')->unsigned()->nullable();
$table->integer('updated_by')->unsigned()->nullable();
$table->integer('deleted_by')->unsigned()->nullable();
$table->foreign('created_by')->references('id')->on('users')
->onDelete('cascade');
$table->foreign('updated_by')->references('id')->on('users')
->onDelete('cascade');
$table->foreign('deleted_by')->references('id')->on('users')
->onDelete('cascade');
$table->foreign('company_id')->references('id')->on('companies')
->onDelete('cascade');
$table->index('nit');
$table->index('name');
$table->index('created_at');
$table->index('deleted_at');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down() {
Schema::dropIfExists('locations');
}
}
公司型号class
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* App\Company
*/
class Company extends Model {
use SoftDeletes;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'nit', 'name', 'contact_name', 'address', 'phone', 'email', 'website',
];
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $dates = ['deleted_at'];
/**
* Get the users for the company.
*/
public function users() {
return $this->hasMany(User::class);
}
/**
* Get the locations for the company.
*/
public function locations() {
return $this->hasMany(Location::class);
}
/**
* Get the invoices for the company.
*/
public function invoices() {
return $this->hasMany(Invoice::class);
}
/**
* Get the user that created the record.
*/
public function createdBy() {
return $this->belongsTo(User::class, 'created_by');
}
/**
* Get the last user that updated the record.
*/
public function updatedBy() {
return $this->belongsTo(User::class, 'updated_by');
}
/**
* Get the user that removed the record.
*/
public function deletedBy() {
return $this->belongsTo(User::class, 'deleted_by');
}
/**
* Scope a query to only include the first active company.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function active($query) {
return $query->orderBy('id')->limit(1);
}
}
位置模型class:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* App\Location
*/
class Location extends Model {
use SoftDeletes;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'nit', 'name', 'contact_name', 'address', 'phone', 'email', 'website', 'company_id',
];
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $dates = ['deleted_at'];
/**
* Get the company that owns the location.
*/
public function company() {
return $this->belongsTo(Company::class);
}
/**
* Get the products for the location.
*/
public function products() {
return $this->hasMany(Product::class);
}
/**
* Get the inventory records for the location.
*/
public function inventories() {
return $this->hasMany(Inventory::class);
}
/**
* Get the user that created the record.
*/
public function createdBy() {
return $this->belongsTo(User::class, 'created_by');
}
/**
* Get the last user that updated the record.
*/
public function updatedBy() {
return $this->belongsTo(User::class, 'updated_by');
}
/**
* Get the user that removed the record.
*/
public function deletedBy() {
return $this->belongsTo(User::class, 'deleted_by');
}
}
提到的检测系统第一个运行的中间件:
<?php
namespace App\Http\Middleware;
use App\Company;
use Closure;
class CheckSystemFirstRun {
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next) {
/** @var \Illuminate\Http\Response $response */
$response = $next($request);
// The verification must be done AFTER the response has been generated, otherwise the request's route is
// unknown.
if ($request->route()->getName() != 'company.create') {
// Check if there are no active companies.
if (Company::count() == 0) {
return redirect(route('company.create'));
}
} else {
// Check if there are active companies.
if (Company::count() > 0) {
return redirect(route('dashboard'));
}
}
return $response;
}
}
允许用户输入第一个公司和位置记录的 CompanyController class:
<?php
namespace App\Http\Controllers;
use App\Company;
use App\Http\Requests\AddCompanyRequest;
use Illuminate\Http\Request;
class CompanyController extends Controller {
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create() {
return view('company.create');
}
/**
* Store a newly created resource in storage.
*
* @param AddCompanyRequest $request
* @param Company $company
* @return \Illuminate\Http\Response
*/
public function store(AddCompanyRequest $request, Company $company) {
$company->create($request->all());
// If there are no locations, create one using the same data as the received to create the company.
if ($company->locations->count() == 0) {
$company->locations()->create($request->all());
}
return redirect()->route('company.create');
}
}
还包含公司创建验证的指定请求 class:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class AddCompanyRequest extends FormRequest {
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() {
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules() {
return [
'nit' => 'required|max:255',
'name' => 'required|max:255',
'email' => 'required|email|unique:companies|max:255',
'website' => 'url|max:255',
];
}
}
当数据库是全新的并且我 运行 系统时,我被重定向到 "Create Company" 操作。提交后,新公司记录创建成功,但创建预期位置记录时与公司记录没有预期关系(company_id 外键列保持 NULL)。
我正在关注 the recommendation from Laravel 5.3,所以我不确定我的代码有什么问题。
在发布此问题之前,我发现位置 table 中的 company_id 字段可能需要在迁移中定义为可空;以前不是这样的,但那时 Laravel/PHP 响应完整性错误 MySQL 因为 "company_id" 字段不能为空。我还尝试 dd() 用于在新记录上定义 company_id 的参数,但返回 id 值的函数始终返回 null。另外,我试过:
$company->locations()->create($request->all());
和
$location = new Location($request->all());
$company->locations()->save($location);
都没有成功。
我正在使用 MySQL Ver 15.1 Distrib 10.1.10-MariaDB,适用于 Windows 10 x64,PHP 7.0.4.
上的 Win32 (AMD64)非常感谢任何帮助。谢谢。
更新 01
这是执行操作时执行的查询的输出:
----------------------
Query: insert into `companies` (`nit`, `name`, `contact_name`, `address`, `phone`, `email`, `website`, `updated_at`, `created_at`) values (?, ?, ?, ?, ?, ?, ?, ?, ?)
Bindings: array (
0 => '1113332323-9',
1 => 'TEST COMPANY INC',
2 => 'JOHN DOE',
3 => '1362 36TH PL',
4 => '8889990099',
5 => 'test@test.com',
6 => 'http://test.com',
7 => '2017-01-16 00:16:25',
8 => '2017-01-16 00:16:25',
)
Time: 4.5099999999999998
----------------------
Query: select * from `locations` where `locations`.`company_id` is null and `locations`.`company_id` is not null and `locations`.`deleted_at` is null
Bindings: array (
)
Time: 0.48999999999999999
----------------------
Query: insert into `locations` (`nit`, `name`, `contact_name`, `address`, `phone`, `email`, `website`, `company_id`, `updated_at`, `created_at`) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Bindings: array (
0 => '1113332323-9',
1 => 'TEST COMPANY INC',
2 => 'JOHN DOE',
3 => '1362 36TH PL',
4 => '8889990099',
5 => 'test@test.com',
6 => 'http://test.com',
7 => NULL,
8 => '2017-01-16 00:16:25',
9 => '2017-01-16 00:16:25',
)
Time: 4.5300000000000002
您正在将位置添加到 Company
的 ORM 实例,而不是新创建的记录。
$company->create($request->all());
应该是
$company = $company->create($request->all());