Laravel - Désireux de Chargement Polymorphe de la Relation d'Modèles associés
Je peux désireux de charge polymorphes relations/modèles sans n+1 points. Cependant, si j'essaie d'accéder à un modèle de la polymorphes modèle, le n+1 problème s'affiche et je n'arrive pas à trouver une solution. Voici la configuration exacte de voir localement:
1) DB nom de la table de données/
history
companies
products
services
2) des Modèles
//History
class History extends Eloquent {
protected $table = 'history';
public function historable(){
return $this->morphTo();
}
}
//Company
class Company extends Eloquent {
protected $table = 'companies';
//each company has many products
public function products() {
return $this->hasMany('Product');
}
//each company has many services
public function services() {
return $this->hasMany('Service');
}
}
//Product
class Product extends Eloquent {
//each product belongs to a company
public function company() {
return $this->belongsTo('Company');
}
public function history() {
return $this->morphMany('History', 'historable');
}
}
//Service
class Service extends Eloquent {
//each service belongs to a company
public function company() {
return $this->belongsTo('Company');
}
public function history() {
return $this->morphMany('History', 'historable');
}
}
3) de Routage
Route::get('/history', function(){
$histories = History::with('historable')->get();
return View::make('historyTemplate', compact('histories'));
});
4) Modèle avec n+1 enregistré uniquement becacuse de $histoire->historable->l'entreprise->nom, commentaire, n+1 s'en va.. mais nous avons besoin d'un lointain liées nom de la société:
@foreach($histories as $history)
<p>
<u>{{ $history->historable->company->name }}</u>
{{ $history->historable->name }}: {{ $history->historable->status }}
</p>
@endforeach
{{ dd(DB::getQueryLog()); }}
J'ai besoin d'être en mesure de charger les noms de la société avec impatience (en une seule requête) que c'est un modèle de la polymorphes modèles de relations Product
et Service
.
J'ai travaillé sur ce projet pendant des jours, mais ne peut pas trouver une solution.
History::with('historable.company')->get()
ignore juste les company
dans historable.company
.
Ce serait une solution efficace à ce problème?
- vous pouvez désireux de charge
Company
modèle dynamiquement par$historables->load('company')
- J'ai ajouté une mise à jour, où serait votre suggestion avides de charger
Company
?
Vous devez vous connecter pour publier un commentaire.
Solution:
Il est possible, si vous ajoutez:
à la fois à l'
Service
etProduct
modèles. De cette façon, lecompany
relation est empressé-chargé à chaque fois unService
ou unProduct
est chargé, y compris lorsqu'il est chargé par le polymorphe relation avecHistory
.Explication:
Cela entraînera un supplément de 2 requêtes, l'une pour
Service
et un pourProduct
, c'est à dire une requête pour chaquehistorable_type
. Si votre nombre total de requêtes—quel que soit le nombre de résultatsn
—va dem+1
(sans désireux de chargement de la lointainecompany
à(m*2)+1
, oùm
est le nombre de modèles liés par votre polymorphes relation.Facultatif:
L'inconvénient de cette approche est que vous avez toujours désireux de charge de la
company
rapport sur laService
etProduct
modèles. Cela peut ou peut ne pas être un problème, en fonction de la nature de vos données. Si c'est un problème, vous pouvez utiliser cette astuce pour automatiquement désireux de chargecompany
seulement lors de l'appel de la polymorphes relation.Ajouter à votre
History
modèle:Maintenant, lorsque vous chargez le
historable
polymorphes égard, Éloquent va chercher les classesServiceWithCompany
etProductWithCompany
, plutôt que deService
ouProduct
. Ensuite, la création de ces classes, et de définirwith
à l'intérieur d'eux:ProductWithCompany.php
ServiceWithCompany.php
...et enfin, vous pouvez supprimer
protected $with = ['company'];
à partir de la baseService
etProduct
classes.Très orthodoxe, mais il devrait fonctionner.
$table
de l'extension de la classe. J'ai de nouveau l'espoir de Taylor nous permet de nous interroger facilement comme d'autres relations comme ce$histories = History::with('historable.company')->get();
Merci encore damiani 🙂$table
seulement si vous avez déclaré explicitement sur le modèle parent; si vous n'avez pas le cas, le polymorphe appel sera à la recherche de la tableproduct_with_company
. Cette charge-lointain-rapport de la fonctionnalité serait un complément utile à Laravel, même si c'est un peu compliqué, car il suppose que la même relation (company
) est disponible sur tous les transformé les modèles.getHistorableTypeAttribute
où avez-vous découvrir de cette. Je n'arrive pas à trouver d'informations à ce sujet. Toute ressource en ligne(s)?getFooAttribute
sera effectué chaque fois que lafoo
de la propriété est accessible, et$this->foo
sera réglé à n'importe quelle valeur est retournée à partir de l'accesseur.Vous pouvez séparer la collection, puis paresseux désireux de charge de chacun:
Probablement ce n'est pas la meilleure solution, en ajoutant
protected $with = ['company'];
comme suggéré par @damiani est aussi une bonne solution, mais cela dépend de votre logique métier.$histories = History::with('historable.company')->get();
Merci pour votre réponse très - upvoted!Pull Request #13737 et #13741 résolu ce problème.
De simplement mettre à jour votre Laravel version et le code suivant
Fonctionnera comme prévu.
Je ne suis pas sûr à 100% sur ce point, car il est difficile de re-créer votre code dans mon système, mais peut-être
belongTo('Company')
devrait êtremorphedByMany('Company')
. Vous pouvez également essayer demorphToMany
. J'ai été en mesure d'obtenir un complexe, polymorphe relation pour charger correctement sans les appels multiples. ?morphToMany
etmorphedByMany
sont utilisés dans un beaucoup beaucoup polymorphes relation, qui n'est pas le cas et nécessite une autre structure de la table.Comme João Guilherme mentionné, ceci a été corrigé dans la version 5.3 Cependant, je me suis trouvé face à la même bug dans une Application où il n'est pas possible de mettre à niveau. J'ai donc créé une remplacer la méthode qui permettra d'appliquer le correctif à l'Héritage des Api. (Merci Jean, pour me pointer dans la bonne direction pour produire ce.)
Tout d'abord, créez votre Override de la classe:
Ensuite, vous aurez besoin de quelque chose qui permet à votre Modèle de classes en parler à votre incarnation de MorphTo plutôt que Éloquent de la. Cela peut être fait soit par un trait appliqué à chaque modèle, ou un enfant d'Éclairer\Database\Éloquent\Modèle qui obtient étendu par votre modèle de classes au lieu d'Éclairer\Database\Éloquent\Modèle directement. J'ai choisi de faire cela en un trait. Mais dans le cas où vous avez choisi de faire un enfant de la classe, je l'ai laissé dans la partie où il déduit le nom comme un heads-up que c'est quelque chose que vous auriez besoin à considérer: