99 votes

Comment accéder à la relation hasMany du modèle avec une condition where?

J'ai créé un modèle Game en utilisant une condition / contrainte pour une relation comme suit :

class Game extends Eloquent {
    // beaucoup plus de choses ici

    // relation sans contraintes... fonctionne bien
    public function videos() {
        return $this->hasMany('Video');
    }

    // entraîne un "problème", voir les exemples ci-dessous
    public function available_videos() {
        return $this->hasMany('Video')->where('available','=', 1);
    }
}

Lorsque je l'utilise de cette manière :

$game = Game::with('available_videos')->find(1);
$game->available_videos->count();

tout fonctionne bien, comme roles est la collection résultante.

MON PROBLÈME :

lorsque j'essaie d'y accéder sans chargement eager

$game = Game::find(1);
$game->available_videos->count();

une Exception est lancée car elle dit "Appel à une fonction membre count() sur un non-objet".

En utilisant

$game = Game::find(1);
$game->load('available_videos');
$game->available_videos->count();

fonctionne bien, mais cela me semble assez compliqué, car je n'ai pas besoin de charger les modèles associés, si je n'utilise pas de conditions dans ma relation.

Ai-je manqué quelque chose ? Comment puis-je m'assurer que les available_videos sont accessibles sans utiliser le chargement eager ?

Pour ceux qui sont intéressés, j'ai également posté ce problème sur http://forums.laravel.io/viewtopic.php?id=10470

121voto

Je pense que c'est la bonne manière :

class Game extends Eloquent {
    // beaucoup d'autres choses ici

    // relation sans contraintes... fonctionne bien
    public function videos() {
        return $this->hasMany('Video');
    }

    // entraîne un "problème", voir les exemples ci-dessous
    public function available_videos() {
        return $this->videos()->where('available','=', 1);
    }
}

Et ensuite vous devrez

$game = Game::find(1);
var_dump( $game->available_videos()->get() );

48voto

Sabrina Gelbart Points 1028

Je pense que c'est ce que vous cherchez (Laravel 4, voir http://laravel.com/docs/eloquent#querying-relations)

$games = Game::whereHas('video', function($q)
{
    $q->where('available','=', 1);

})->get();

48voto

xsilen T Points 130

//utilisez getQuery() pour ajouter une condition

public function videos() {
    $instance = $this->hasMany('Video');
    $instance->getQuery()->where('available','=', 1);
    return $instance
}

// simplement

public function videos() {
    return $this->hasMany('Video')->where('available','=', 1);
}

18voto

nikunj kansara Points 1288

Si vous souhaitez appliquer une condition sur la table relationnelle, vous pouvez également utiliser d'autres solutions. Cette solution fonctionne de mon côté.

public static function getAllAvailableVideos() {
        $result = self::with(['videos' => function($q) {
                        $q->select('id', 'name');
                        $q->where('available', '=', 1);
                    }])
                ->get();
        return $result;
    }

16voto

Remluben Points 135

Juste au cas où quelqu'un d'autre rencontre les mêmes problèmes.

Notez que les relations doivent être en camelcase. Donc dans mon cas, available_videos() aurait dû être availableVideos().

Vous pouvez facilement le découvrir en enquêtant dans la source de Laravel :

// Illuminate\Database\Eloquent\Model.php
...
/**
 * Obtenir un attribut du modèle.
 *
 * @param  string  $key
 * @return mixed
 */
public function getAttribute($key)
{
    $inAttributes = array_key_exists($key, $this->attributes);

    // Si la clé fait référence à un attribut, nous pouvons simplement retourner la
    // valeur d'attribut brut du modèle. Cela permet à chaque attribut d'être
    // accédé dynamiquement via la méthode _get sans accesseurs.
    if ($inAttributes || $this->hasGetMutator($key))
    {
        return $this->getAttributeValue($key);
    }

    // Si la clé existe déjà dans le tableau des relations, cela signifie simplement que la
    // relation a déjà été chargée, donc nous la retournerons simplement car il n'y a pas besoin de 
    // interroger à nouveau les relations deux fois.
    if (array_key_exists($key, $this->relations))
    {
        return $this->relations[$key];
    }

    // Si l'"attribut" existe en tant que méthode sur le modèle, nous supposerons simplement
    // qu'il s'agit d'une relation et chargerons et retournerons les résultats de la requête
    // et hydraterons la valeur de la relation dans le tableau des "relations".
    $camelKey = camel_case($key);

    if (method_exists($this, $camelKey))
    {
        return $this->getRelationshipFromMethod($key, $camelKey);
    }
}

Cela explique également pourquoi mon code fonctionnait, chaque fois que je chargeais les données en utilisant la méthode load() auparavant.

Quoi qu'il en soit, mon exemple fonctionne parfaitement maintenant, et $model->availableVideos retourne toujours une Collection.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X