Я только начинаю с проекта на основе Laravel, и у меня возникают проблемы с созданием экземпляра модели, связанного с другим. Таким образом, у меня есть класс модели «Компания», который ссылается на таблицу «компании» MySQL, а также класс модели «Местоположение», ссылающийся на таблицу «местоположения». Обе таблицы связаны между собой (у Компании много мест, каждое Место принадлежит Компании). Все идет нормально.Ошибка создания связанных экземпляров модели в Laravel 5.3
У меня есть механизм «промежуточного ПО», проверяющий существование хотя бы одной компании, и если нет компаний, я предполагаю, что это первый раз, когда запускается система, поэтому я показываю контроллер/действие «Создать компанию», поэтому что пользователь создает первую компанию. В представлении это также создает единую запись местоположения, используя ту же самую информацию о компании, , поэтому в конце записи местоположения в базе данных должно быть указано как «company_id» идентификатор записи компании, которая была только что создана, но этого не происходит ,
Позвольте мне показать существующие файлы и классы, имеющие отношение к этой проблеме: файл
миграции для создания компаний таблицы:
<?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');
}
}
файл миграции для создания месторасположении таблицы:
<?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');
}
}
Модель компании класс
<?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);
}
}
местоположение класс модель:
<?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 который позволяет пользователю вводить первый Компания и Место записи:
<?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');
}
}
Указанный класс Request, который содержит также создание валидаций Компания:
<?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',
];
}
}
Когда база данных является новой, и я запустить систему, я переадресованные действие «Создать компанию». При отправке нового отчета о компании создается успешно, но ожидаемая запись местоположения создается БЕЗ ожидаемых отношений с записью компании (во внешнем столбце company_id хранится NULL).
Я следую the recommendation from Laravel 5.3 поэтому я не уверен, что случилось с моим кодом.
Перед тем, как этот вопрос был отправлен, я обнаружил, что поле company_id в таблице местоположений может потребоваться определить как NULL в миграциях; раньше это было не так, но тогда 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 Distributed 10.1.10-MariaDB для Win32 (AMD64) для Windows 10 x64, PHP 7.0.4.
Любая помощь очень ценится. Благодарю.
Update 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 => '[email protected]',
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 => '[email protected]',
6 => 'http://test.com',
7 => NULL,
8 => '2017-01-16 00:16:25',
9 => '2017-01-16 00:16:25',
)
Time: 4.5300000000000002
За исключением нескольких не связанных между собой улучшений, код создания места выглядит правильно (я создал минимальный пример локально и 'company_id' назначен правильно). Вы можете проверить свой журнал запросов и опубликовать результаты? – nCrazed
Также вам не нужно иметь столбец FK как nullable (если у вас нет варианта использования для местоположения без компании), поскольку '$ company :: locations() -> create ([])' должно назначать правильное значение fk , – nCrazed
@ nCrazed, пожалуйста, проверьте раздел «Обновление 01» в главном содержимом вопроса. Я просто добавил выход журнала в соответствии с запросом. В запросе вставки отсутствует значение company_id. Что касается вашего комментария о nullable company_id, я согласен с вами. Если я удаляю флаг nullable из файла миграции и обновляюсь, упомянутый процесс генерирует исключение нарушения целостности, потому что запись местоположения вставляется без требуемой company_id. –