1
votes

Laravel appartientToMany relation avec un seul résultat

J'ai une relation entre deux tables avec une table de jointure qui n'a qu'un seul résultat.

Lorsque je définis une relation Laravel appartientToMany, au lieu de renvoyer une collection avec un seul élément, j'aimerais qu'elle renvoie cet élément seul.

Existe-t-il un moyen de modéliser cela dans Laravel? p>

Merci d'avance.

[EDIT↑

Je vais essayer d'expliquer ce que je veux en utilisant l'exemple classique Users / Roles. Outre les tables users et roles , nous aurons un tableau croisé dynamique users_roles qui stockera tous les rôles de l'utilisateur. Un utilisateur peut, à tout moment, n'avoir qu'un seul rôle actif (identifié par l'attribut active étant true ).

class User {
    function role() {
        return $this->belongsToMany('App\Role')->wherePivot('active', 'true');
    }
}

Avec cette définition de relation, lorsque j'accède à $ user-> role , j'obtiens une collection (avec un seul élément) de rôles. Ce que je voudrais, c'est avoir cette instance de rôle directement.


3 commentaires

montre-nous du code que tu as atteint jusqu'ici


Veuillez afficher les données des tableaux et les codes modèles Laravel


Votre tableau croisé dynamique a-t-il des attributs supplémentaires (en plus des clés étrangères)?


4 Réponses :


5
votes

Je ne sais pas pourquoi vous avez appartientToMany si vous n'avez besoin que d'une seule relation, mais le code ci-dessous vous aidera:

public function getSpecificProductAttribute()
{
    return $this->products()
                ->where('column','value')->first();
}

OU

public function products()
{
    return $this->belongsToMany('App\Product');
}

public function specific_product()
{
    return $this->products()
                ->where('column','value')->first();
}


3 commentaires

J'ai plusieurs entrées dans le tableau croisé dynamique mais une seule est valide et c'est celle que je veux retourner. J'aime votre approche.


Le "problème" avec cette solution est que je ne peux pas y accéder en tant que propriété, comme les relations normales ...


puis changez specific_product en getSpecificProduct puis vous pouvez obtenir directement le modèle parent



1
votes

J'ai rencontré exactement le même problème, laissez-moi vous montrer comment je l'ai géré.

Dans mon cas, j'ai une relation appartToMany entre les matériaux et les types de clients, le tableau croisé dynamique contient le prix des matériaux pour des types de clients spécifiques, donc autant d'enregistrements (prix) dans le tableau croisé dynamique que de types_client.

Ce à quoi je m'attendais: lorsqu'un prix est demandé pour un type de client spécifique, je veux obtenir le prix défini pour cela type_client spécifique comme élément imbriqué .

Ce que j'ai obtenu: une collection avec un seul élément.

Voici ce que je avait au début dans mon modèle:

$resources = Material::with(['scoped_price' => function($query) use ($customer_type_id){
                        $query->where('customertype_id', $customer_type_id);
                       }])->get();

Bien sûr, lorsque j'ai demandé les types de client pour un type de client spécifique, le résultat n'était pas celui attendu:

public function scoped_price(){
    return $this->belongsTo('App\CustomertypeMaterial', 'id','material_id');
}

Il retournait un modèle Material avec une collection imbriquée customer_types avec 1 élément, me forçant à utiliser first () ou à boucler sur 1 élément.

Le solution: créez un modèle qui étend t le tableau croisé dynamique et y ajouter une relation.

Création d'un nouveau modèle qui étend le pivot:

use Illuminate\Database\Eloquent\Relations\Pivot;

class CustomertypeMaterial extends Pivot
{
    protected $table    = 'customertype_material';
    protected $fillable = ['price', 'customertype_id', 'material_id'];
}

Maintenant, ajout d'une relation pointant vers ce nouveau model dans mon Material Model:

$resources = Material::with(['customer_types' => function($query) use ($customer_type_id){
                        $query->where('customertype_id', $customer_type_id);
                       }])->get();

Enfin la requête chargeant cette nouvelle relation:

class Material extends Model
{
    public function customer_types(){
        return $this->belongsToMany('App\CustomerType', 'customertype_material', 'material_id', 'customertype_id')->withPivot('price');
    }
}

Le résultat est un Material model avec un élément scoped_price imbriqué dessus et filtré par un customer_type_id

Je ne suis pas sûr que ce soit la bonne façon de faire cela, mais cela fonctionne pour moi.

J'espère que cela aide!


0 commentaires

-1
votes

Laravel Eloquent travaille sur le principe de la magie. Vous pouvez remplacer la méthode magique __get . S'il n'y a pas de propriété, la méthode __get est appelée:

(dans votre modèle)

    public function __get ($name)
    {
        $answer = parent::__get($name);
        if($name=='your_prop'){
            $answer=!empty($answer)?$answer[0]:null;
        }
        return $answer;
    }

Si votre your_prop code> relation à plusieurs renvoie tout ce qui prend le premier dans le tableau.



2
votes

J'ai rencontré ce problème et j'ai trouvé un moyen très propre de le résoudre.

Tout d'abord, changez le nom de la fonction d'accesseur qui renvoie le résultat appartientToMany pour refléter le fait que ceux-ci retournent résultats multiples. Dans votre cas, cela signifierait utiliser des rôles au lieu de role:

protected $appends = ['role'];

public function getRoleAttribute() {
  return $this->roles()->first();
}

Ensuite, ajoutez ce qui suit à votre modèle:

function roles() {
  return $this->belongsToMany('App\Role')->wherePivot('active', 'true');
}

Maintenant, lorsque vous appelez $ user-> role , vous obtenez le premier élément.


0 commentaires