Laravel relación padre/hijo en el mismo modelo

4 minutos de lectura

avatar de usuario
amor

La configuración y los datos ficticios

Tengo un modelo simple llamado Categoría, que tiene el siguiente esquema:

|----------------------------------------------|
| cat_id   | cat_name    | parent_id           |
|----------------------------------------------|
|   1      | Home        |   0                 |
|----------------------------------------------|
|   2      | Products    |   1                 |
|----------------------------------------------| 
|   3      | Services    |   1                 |
|----------------------------------------------|
|   4      | Product A   |   2                 |
|----------------------------------------------|
|   5      | Product B   |   2                 |
|----------------------------------------------|

La salida deseada

Entonces puede ver que obtendríamos una jerarquía muy sencilla de la siguiente manera:

Home
  - Products
      - Product A
      - Product B
  - Services

La cuestión

Estoy tratando de mapear esta relación en Laravel 4.2, para poder consultar un modelo y obtener su padre (siempre tendrá un padre) y categorías secundarias si existen.

He definido la relación en el modelo de Categoría usando:

public function children()
{
    return $this->hasMany('Category', 'parent_id', 'cat_id');
}
public function parent()
{
    return $this->belongsTo('Category', 'parent_id');
}

El problema

Puedo hacer que el nombre de los padres funcione, usando

$category = Category::findOrFail($id);
return $category->parent->cat_name;

Sin embargo, no entiendo cómo obtener los objetos secundarios.

He intentado:

$category = Category::findOrFail($id);
$children = $category->children();

Pero cuando dd ($ niños) no genera lo que esperaría.

  • ¿Cuál es la salida de $niños?

    – DouglasDC3

    7 de enero de 2015 a las 10:07

avatar de usuario
lukasgeiter

Llamando a la función de relación (->children()) devolverá una instancia de la clase de relación. Entonces tienes que llamar get() o simplemente usa la propiedad:

$children = $category->children()->get();
// or
$children = $category->children;

Explicación adicional

Realmente children() y children son algo bastante diferente. children() simplemente llama al método que definió para su relación. El método devuelve un objeto de HasMany. Puede usar esto para aplicar más métodos de consulta. Por ejemplo:

$category->children()->orderBy('firstname')->get();

Ahora accediendo a la propiedad children funciona de manera diferente. Nunca lo definiste, por lo que Laravel hace algo de magia en segundo plano.

echemos un vistazo a Illuminate\Database\Eloquent\Model:

public function __get($key)
{
    return $this->getAttribute($key);
}

los __get La función se llama cuando intenta acceder a una propiedad en un objeto PHP que en realidad no existe.

public function getAttribute($key)
{
    $inAttributes = array_key_exists($key, $this->attributes);

    // If the key references an attribute, we can just go ahead and return the
    // plain attribute value from the model. This allows every attribute to
    // be dynamically accessed through the _get method without accessors.
    if ($inAttributes || $this->hasGetMutator($key))
    {
        return $this->getAttributeValue($key);
    }

    // If the key already exists in the relationships array, it just means the
    // relationship has already been loaded, so we'll just return it out of
    // here because there is no need to query within the relations twice.
    if (array_key_exists($key, $this->relations))
    {
        return $this->relations[$key];
    }

    // If the "attribute" exists as a method on the model, we will just assume
    // it is a relationship and will load and return results from the query
    // and hydrate the relationship's value on the "relationships" array.
    $camelKey = camel_case($key);

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

luego en getAttribute primero hay un código que verifica los atributos “normales” y luego regresa. Y finalmente, al final del método, si hay un método de relación definido getRelationshipFromMethod se llama.

Luego recuperará el resultado de la relación y lo devolverá.

  • Ok, entonces los paréntesis en la llamada de niños no son necesarios en este caso. Algo confuso en un marco por lo demás muy hermoso, pero eso resolvió el problema. Gracias.

    – Amo

    7 de enero de 2015 a las 10:13

  • Realmente no … Actualicé mi respuesta con una explicación 🙂

    – lukasgeiter

    7 de enero de 2015 a las 10:22

  • ¡Perfecto! Gracias por la explicación tan detallada.

    – Amo

    7 de enero de 2015 a las 10:42

Establece esto en el modelo y prueba:

public function children()
{
    return $this->hasMany(self::class, 'parent_id');
}

public function grandchildren()
{
    return $this->children()->with('grandchildren');
}

  • Affarin, eyval 😉

    – Dol Durma

    27 de agosto de 2021 a las 7:18

  • jaja 🙂 @DolDurma

    – Zahra Badri

    28 de agosto de 2021 a las 6:10

  • Adam eftekhar mikone estar en javanan irani 🙂

    – Mostafá Norzade

    28 oct 2021 a las 8:48

  • @MostafaNorzade mammoonam 🙂

    – Zahra Badri

    30 oct 2021 a las 8:38

¿Ha sido útil esta solución?