2017-01-15 4 views
0

Ich beginne gerade mit einem Laravel-basierten Projekt und habe Probleme beim Erstellen einer Modellinstanz, die sich auf eine andere bezieht. Zusammengefasst habe ich eine Modellklasse "Firma", die auf eine MySQL-Tabelle "Unternehmen" verweist, und auch eine Modellklasse "Standort", die auf eine Tabelle "Standorte" verweist. Beide Tabellen sind verwandt (ein Unternehmen hat viele Standorte, jeder Standort gehört einem Unternehmen). So weit, ist es gut.Fehler beim Erstellen verwandter Modellinstanzen in Laravel 5.3

Ich habe einen "Middleware" -Mechanismus, der die Existenz von mindestens einer Firma überprüft, und wenn es keine Firmen gibt, nehme ich an, dass das System das erste Mal ausgeführt wird, so zeige ich die "Create Company" -Controller/Aktion an dass der Benutzer die erste Firma erstellt. Beim Senden erstellt dies auch einen einzelnen Standortdatensatz unter Verwendung der gleichen Unternehmensinformationen, , so dass der Standortdatensatz in der Datenbank als "company_id" die ID des Unternehmensdatensatzes haben sollte, der gerade erstellt wurde. Dies geschieht jedoch nicht .

Lassen Sie mich die vorhandenen Dateien und Klassen relevant für dieses Problem anzuzeigen:

Migration Datei, um die Firmen-Tabelle zu erstellen:

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

Migration Datei, um die Standorte Tabelle zu erstellen:

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

Firmenmodellklasse

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

Die Location Modellklasse:

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

Die genannte Middleware den ersten Lauf des Systems zu erkennen:

<?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; 
    } 
} 

Die CompanyController Klasse was dem Benutzer erlaubt, den ersten einzugeben Unternehmen und Standort Datensätze:

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

Die angegebene Anfrage Klasse, die auch die Gesellschaft Schöpfung Validierungen enthält:

<?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', 
     ]; 
    } 

} 

Wenn die Datenbank ist brandneu und ich laufe das System, ich umgeleitet bin zu die Aktion "Firma erstellen". Beim Senden wird der neue Firmeneintrag erfolgreich erstellt, aber der erwartete Standortdatensatz wird OHNE die erwartete Beziehung mit dem Unternehmensdatensatz erstellt (Fremdschlüssel Spalte firma_id behält NULL bei).

Company record successfully created

Location record created without the expected company_id value

Ich folge the recommendation from Laravel 5.3 so bin ich nicht sicher, was mit meinem Code falsch ist.

Bevor diese Frage gestellt wurde, habe ich festgestellt, dass das Feld company_id in der Standorttabelle in den Migrationen möglicherweise als nullable definiert werden kann; Das war vorher nicht so, aber Laravel/PHP reagierte damals auf einen Integritätsfehler mit MySQL, weil das Feld "company_id" nicht null sein kann. Ich habe auch versucht, dd() die Parameter zu definieren, die verwendet werden, um die company_id auf dem neuen Datensatz zu definieren, aber die Funktion, die den ID-Wert zurückgibt, gab immer null zurück. Auch ich habe versucht:

$company->locations()->create($request->all()); 

und

$location = new Location($request->all()); 
$company->locations()->save($location); 

beide ohne Erfolg.

Ich verwende MySQL Ver 15.1 Distrib 10,1.10-MariaDB, für Win32 (AMD64) auf Windows 10 x64, PHP 7.0.4.

Jede Hilfe wird sehr geschätzt. Vielen Dank.


aktualisieren 01

Hier ist die Ausgabe der ausgeführten Abfragen, wenn die Aktion ausgeführt wird:

---------------------- 
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 
+0

Abgesehen sein, sieht die Lage Erstellungscode richtig (Ich habe ein minimales Beispiel lokal und 'company_id' zugeordnet ist korrekt). Können Sie Ihr Abfrageprotokoll überprüfen und die Ergebnisse veröffentlichen? – nCrazed

+0

Sie sollten auch nicht die FK-Spalte als Nullwert haben (es sei denn, Sie haben einen Anwendungsfall für einen Standort ohne Firma), da '$ company :: locations() -> create ([])' den richtigen fk-Wert zuweisen sollte . – nCrazed

+0

@ nCrazed, Bitte überprüfen Sie den Abschnitt "Update 01" im Hauptfrageinhalt. Ich habe gerade die Log-Ausgabe wie gewünscht hinzugefügt. Der Einfügeabfrage fehlt der Wert Company_id. Was Ihren Kommentar zur nullbaren company_id betrifft, stimme ich Ihnen zu. Wenn ich das NULL-Flag aus der Migrationsdatei entferne und aktualisiere, generiert der genannte Prozess eine Integritätsverletzungsausnahme, weil der Standortdatensatz ohne die erforderliche company_id eingefügt wird. –

Antwort

1

Sie fügen den Standort zu Ihrem ORM Instanz Company auf nicht der neu erstellter Datensatz

$company->create($request->all()); 

sollte von einigen unabhängigen Verbesserungen

$company = $company->create($request->all()); 
+0

Das ist die Antwort. Ich dachte, die Methode create/save würde dieselbe $ Firmeninstanz aktualisieren, ohne das Ergebnis derselben $ Unternehmensvariable zuordnen zu müssen. Danke vielmals. –