J'ai la disposition de table suivante:
deals:
- id
- price
products:
- id
- name
deal_product:
- id
- deal_id
- product_id
metrics:
- id
- name
metric_product:
- id
- metric_id
- product_id
- value
products
et metrics
ont une relation plusieurs-à-plusieurs avec une colonne pivot de valeur.
deals
et products
ont également une relation plusieurs-à-plusieurs.
Je peux obtenir des statistiques pour un produit avec $product->metrics
, mais je veux pouvoir obtenir toutes les statistiques pour tous les produits liés à un accord, donc je pourrais faire quelque chose comme ceci: $deal->metrics
.
J'ai actuellement les éléments suivants dans mon modèle Deal
:
public function metrics()
{
$products = $this->products()->pluck('products.id')->all();
return Metric::whereHas('products', function (Builder $query) use ($products) {
$query->whereIn('products.id', $products);
});
}
Mais cela ne renvoie pas une relation, donc je ne peux pas le charger ou obtenir des modèles connexes.
Il doit être au format relationnel, car ils doivent être chargés pour mon cas d'utilisation.
Merci de votre aide!
Si vous souhaitez avoir une relation personnalisée, vous pouvez créer vos propres étendues à la classe abstraite Relation. Par exemple: BelongsToManyThought
.
Mais si vous ne voulez pas implémenter une Relation, je pense qu'elle peut répondre à vos besoins:
Dans App\Deal.php, vous pouvez combiner la solution de @ thomas-van-der-veen
public function metrics()
{
return Metric
::join('metric_product', 'metric.id', '=', 'metric_product.metric_id')
->join('products', 'metric_product.product_id', '=', 'products.id')
->join('deal_product', 'products.id', '=', 'deal_product.product_id')
->join('deals', 'deal_product.deal_id', '=', 'deal.id')
->where('deal.id', $this->id);
}
// you can access to $deal->metrics and use eager loading
public function getMetricsAttribute()
{
if (!$this->relationLoaded('products') || !$this->products->first()->relationLoaded('metrics')) {
$this->load('products.metrics');
}
return collect($this->products->lists('metrics'))->collapse()->unique();
}
Vous pouvez vous référer à ceci post pour voir comment vous pouvez simplement utiliser des relations imbriquées.
Cette solution peut faire l'affaire pour interroger la relation avec la méthode et l'accès à l'attribut de métriques.
Il existe un Laravel 5.5 composer package qui peut effectuer des relations à plusieurs niveaux (approfondi)
Paquet: https://github.com/staudenmeir/eloquent-has-many-deep
Exemple:
User
→ appartient à plusieurs → Role
→ appartient à plusieurs → Permission
class User extends Model
{
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
public function permissions()
{
return $this->hasManyDeep(
'App\Permission',
['role_user', 'App\Role', 'permission_role'], // Pivot tables/models starting from the Parent, which is the User
);
}
}
Exemple si des clés étrangères doivent être définies:
https://github.com/staudenmeir/eloquent-has-many-deep/issues/7#issuecomment-43147794
Cela devrait faire l'affaire:
Le fichier Models/Product.php:
class Product extends Model
{
public function deals()
{
return $this->belongsToMany(Deal::class);
}
public function metrics()
{
return $this->belongsToMany(Metric::class);
}
}
le fichier Models/Deal.php:
class Deal extends Model
{
public function products()
{
return $this->belongsToMany(Product::class);
}
}
le fichier Models/Metric.php:
class Metric extends Model
{
public function products()
{
return $this->belongsToMany(Product::class);
}
}
Depuis votre contrôleur:
class ExampleController extends Controller
{
public function index()
{
$deal = Deal::with('products.metrics')->get();
}
}
Vous disposez désormais d'une collection d'offres sur les produits et de leurs statistiques associées. Voir docs pour un chargement rapide imbriqué.
Je n'ai pas assez de représentants pour poster un commentaire, je vais donc poster une réponse à la place.
Il y a quelques jours, j'ai posté un question similaire et j'ai pu y répondre moi-même. Je pense que la solution fonctionne également pour cette question.
En joignant les tables manuellement et en ajoutant une clause where, vous pouvez récupérer les métriques souhaitées.
Solution possible:
// Deal.php
public function metrics()
{
return Metric
::join('metric_product', 'metric.id', '=', 'metric_product.metric_id')
->join('products', 'metric_product.product_id', '=', 'products.id')
->join('deal_product', 'products.id', '=', 'deal_product.product_id')
->join('deals', 'deal_product.deal_id', '=', 'deal.id')
->where('deal.id', $this->id);
}
// How to retrieve metrics
$deal->metrics()->get();
// You can still use other functions like (what I needed) pagination
$deal->metrics()->paginate(24);
// Or add more where clauses
$deal->metrics()->where(blablabla)->get();
J'espère que cela t'aides :)
Modifier après le commentaire @ntzm
Si vous n'avez besoin que de certaines propriétés de la table de mesures et d'aucune des fonctionnalités du modèle, vous pouvez ajouter une sélection ('*') avant les jointures.
Comme ça:
return Metric
::select('*')
->join(...........
Il renverra un modèle métrique avec tous les attributs des tables jointes.
Cet exemple retournera un objet comme:
Metric (
....
attributes: [
id => 0, // From metrics table
name => 'somename', // Metrics table
metric_id => 0, // metric_product table
product_id => 0, // metric_product table
....
]
....
)
Puisqu'il y a beaucoup de colonnes avec le même nom, vous ne pourrez pas accéder à ces valeurs. Cependant, vous pouvez effectuer plusieurs sélections et renommer les colonnes dans vos résultats.
Comme ça:
retrun Metric
::select([
*,
DB::raw('products.name as product_name'),
....
])
->join(....
Cela se traduira par quelque chose comme:
Metric (
....
attributes: [
id => 0, // From metrics table
name => 'somename', // Metrics table
metric_id => 0, // metric_product table
product_id => 0, // metric_product table
product_name => 'somename' // products table
....
]
....
)
J'espère que cela résoudra quelque chose :) Bien sûr, il existe des moyens beaucoup plus propres de résoudre votre problème.
Il n'y a pas de méthode "plusieurs à plusieurs".
Pour obtenir toutes les métriques disponibles dans tous les produits pour une certaine offre, créez un getter pour Eager Load all Products and Metrics for the Deal, et tirez parti des méthodes de collecte de Laravel.
Dans votre modèle Deal
:
public function products()
{
return $this->belongsToMany('App\Product');
}
public function getMetrics()
{
return $this->products->lists('metrics')->collapse()->values();
}
Vous pouvez ensuite obtenir les métriques d'un accord avec $deal->getMetrics()
Cela renverra une collection d'objets de métrique, qui devrait être exactement ce que vous recherchez.
Si quelqu'un en a encore besoin:
Vous devez d'abord charger les produits et les métriques
$deals->with('products.metrics');
dans le modèle d'accord
public function getMetricsAttribute(){
return $this->products->pluck('metrics')->collapse();
}
vous pouvez désormais obtenir toutes les métriques des produits d'un accord en:
$deal->metrics
Si quelqu'un fait encore des recherches à ce sujet, consultez ce package que j'ai récemment développé. Il vous fournit une relation belongsToManyThrough()
et est super facile à installer et à utiliser :)