2014-07-12 17 views
6

Sto costruendo un CMS package for Laravel.Laravel Eloquent e Namespaces Issues

Tutti i miei models in questo pacchetto sono vincolati e risolti dal contenitore IoC in modo che possano essere facilmente sovrascritti in ogni singola distribuzione del pacchetto.

Per le relazioni non polimorfiche, questo ha funzionato.

Per esempio, un Page ha molti PageModules, per cui il suo rapporto è cambiato da:

// \Angel\Core\Page 

public function modules() 
{ 
    return $this->hasMany('PageModule'); 
} 

a:

// \Angel\Core\Page 

public function modules() 
{ 
    return $this->hasMany(App::make('PageModule')); 
} 

Ma io non sono stati in grado di capire come fare il stessa cosa con le relazioni polimorfiche.

Ad esempio, i Menu contengono MenuItems e ciascun MenuItem può essere associato a un singolo altro modello, ad esempio una Pagina o BlogPost.

Per fare questo il modo in cui laravel, ho aggiunto la seguente relazione al MenuItem:

// \Angel\Core\MenuItem 

public function linkable() 
{ 
    return $this->morphTo(); 
} 

E questo rapporto LinkableModel, che tutti i modelli come pagina e BlogPost estendono:

// \Angel\Core\LinkableModel 

public function menuItem() 
{ 
    return $this->morphOne(App::make('MenuItem'), 'linkable'); 
} 

E la tabella menus_items ha queste righe:

linkable_type  | linkable_id 
-------------------|-------------- 
\Angel\Core\Page | 11 
\Angel\Core\Page | 4 

Questo funziona benissimo, ma ho bisogno del linkable_type di dire "Pagina" invece di "\ Angel \ Core \ Page" e di essere risolto dalla "Pagina" di IoC invece di essere codificato in una particolare classe con spazi dei nomi.

che cosa ho provato:

Secondo this question, dovrebbe essere facile come la definizione di una proprietà $morphClass al collegabile() classi, in questo modo:

// \Angel\Core\Page 
protected $morphClass = 'Page'; 

Ma quando si applica questo e modificare la tabella menus_items simile al seguente:

linkable_type | linkable_id 
---------------|-------------- 
Page   | 11 
Page   | 4 

... Ho semplicemente un errore Class 'Page' not found. ogni volta che linkable() viene chiamato su MenuItem.

This is the exact line in Eloquent that throws the error.

Così, ho scavato nel eloquente e pensato che avrei potuto essere in grado di farla franca con qualcosa di simile:

// \Angel\Core\MenuItem 

public function linkable() 
{ 
    return $this->morphTo(null, App::make($this->linkable_type)); 
} 

... questo si sente così vicino, ma ahimè: Eloquente chiama linkabile() prima di riempire il resto degli attributi/colonne di MenuItem, quindi $ this-> linkable_type è null e quindi non risolverà nulla dal IoC.

Grazie mille in anticipo per qualsiasi consiglio si possa avere!

risposta

4
public function linkable() 
{ 
    return $this->morphTo(null, App::make($this->linkable_type)); 
} 

Questo non funziona in ogni caso, perché morphTo() in Illuminate\Database\Eloquent\Model aspetta

  1. Il nome della relazione polimorfica ('collegabile')
  2. Il tipo del oggetto da modificare ('Pagina', non un'istanza di Page)
  3. Il id dell'oggetto da trasformato

Se non vengono forniti, laravel è abbastanza intelligente da indovinare loro e quindi di conseguenza restituire un oggetto Illuminate\Database\Eloquent\MorphTo.

Inoltre, $this->linkable_type e $this->linkable_id dovrebbe in realtà non essere null in quel contesto.

Diamo un rapido sguardo la parte rilevante della funzione morphTo():

$instance = new $class; 

return new MorphTo(
    $instance->newQuery(), $this, $id, $instance->getKeyName(), $type, $name 
); 

Nota: Questo è il codice a partire dalla versione 4.2.6, il codice linkato sopra sembra essere da un versione successiva ed è leggermente diverso e la funzione restituisce un valore BelongsTo anziché un oggetto MorphTo.

Il problema è in particolare lo $instance = new $class; - la classe è semplicemente instanciata e non risolta. Ma si può semplicemente prendere quella parte della magia e gestire da soli:

public function linkable() 
{ 
    $instance = App::make($this->linkable_type); 

    $id = 'linkable_id'; 
    $type = 'linkable_type'; 
    $name = 'linkable'; 

    return new MorphTo(
     $instance->newQuery(), $this, $id, $instance->getKeyName(), $type, $name 
    ); 
} 

Questo dovrebbe effettivamente funzionare (non hanno provato), ma non sono sicuro di eventuali effetti collaterali che potrebbe causare in qualche bordo casi.

O forse si potrebbe anche semplicemente ignorare l'intera funzione nella vostra MenuItem -model e basta regolare la parte rilevante:

public function morphTo($name = null, $type = null, $id = null) 
{ 
    if (is_null($name)) 
    { 
     list(, $caller) = debug_backtrace(false); 

     $name = snake_case($caller['function']); 
    } 

    list($type, $id) = $this->getMorphs($name, $type, $id); 

    //eager loading 
    if (is_null($class = $this->$type)) 
    { 
     return new MorphTo(
      $this->newQuery(), $this, $id, null, $type, $name 
     ); 
    } 

    // normal lazy loading 
    else 
    { 
     // this is the changed part 
     $instance = \App::make($class); // new $class; 

     return new MorphTo(
      $instance->newQuery(), $this, $id, $instance->getKeyName(), $type, $name 
     ); 
    } 
} 

Nota: Questo funziona bene per lazy-caricamento, ma non lo fa lavoro per il caricamento avido. An issue has been raised seeking a solution for eager-loading here.

Il rapporto sarebbe allora come al solito:

public function linkable() 
{ 
    return $this->morphTo(); 
} 
+0

Sei un genio. Grazie mille per il vostro aiuto. Dici che non possiamo passare nulla per nome perché non sarebbe in grado di dedurlo, ma [questo mi ha fatto pensare che potremmo!] (Http://laravel.com/api/source-class- Illuminate.Database.Eloquent.Model.html # 523) – Leng

+0

Anche se sto usando Laravel 4.1 che ha [questo morphTo] (https://github.com/laravel/framework/blob/2f1c6cd51dfd51d573977f07e34ba609cc0bf3ac/src/Illuminate/Database/Eloquent/ model.php # L724). – Leng

+0

Ratti!Quasdunk, quando carico questa relazione, non funziona più perché $ this-> linkable_type è, in effetti, nullo in quella situazione. Suppongo che durante il caricamento ansioso, Eloquent chiama i metodi di relazione prima di riempire le proprietà/colonne. :(Continuerò a cercare una soluzione funzionante – Leng