php - hasmany - relaciones laravel
Actualizando las marcas de tiempo en la conexión/separación de relaciones Eloquent (3)
Estoy usando Laravel 4 y tengo 2 modelos:
class Asset extends /Eloquent {
public function products() {
return $this->belongsToMany(''Product'');
}
}
class Product extends /Eloquent {
public function assets() {
return $this->belongsToMany(''Asset'');
}
}
Product
tiene las marcas de tiempo estándar en él ( created_at
, updated_at
) y me gustaría actualizar el campo updated_at
del Product
cuando adjunto / separe un Asset
.
Intenté esto en el modelo de Asset
:
class Asset extends /Eloquent {
public function products() {
return $this->belongsToMany(''Product'')->withTimestamps();
}
}
... pero eso no hizo nada en absoluto (aparentemente). Editar : aparentemente esto es para actualizar las marcas de tiempo en la tabla dinámica, no para actualizarlas en la tabla de la relación (es decir, actualiza assets_products.updated_at
, not products.updated_at
).
Luego probé esto en el modelo de Asset
:
class Asset extends /Eloquent {
protected $touches = [ ''products'' ];
public function products() {
return $this->belongsToMany(''Product'');
}
}
... que funciona, pero luego rompe mi semilla que llama a Asset::create([ ... ]);
porque aparentemente Laravel intenta llamar a ->touchOwners()
en la relación sin verificar si es null
:
Error grave de PHP: llamada al método no definido Illuminate / Database / Eloquent / Collection :: touchOwners () en /directorio/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php en la línea 1583
El código que estoy usando para agregar / eliminar Asset
s es este:
Product::find( $validId )->assets()->attach( $anotherValidId );
Product::find( $validId )->assets()->detach( $anotherValidId );
¿Dónde estoy equivocado?
Puedes hacerlo manualmente usando el método touch
:
$product = Product::find($validId);
$product->assets()->attach($anotherValidId);
$product->touch();
Pero si no desea hacerlo manualmente cada vez, puede simplificar este método de creación en su modelo de Product
esta manera:
public function attachAsset($id)
{
$this->assets()->attach($id);
$this->touch();
}
Y ahora puedes usarlo de esta manera:
Product::find($validId)->attachAsset($anotherValidId);
Lo mismo que puedes hacer para separar la acción.
Y noté que tienes una relación belongsToMany
y la otra a hasMany
. Debe ser bastante belongsToMany
a belongsToMany
en ambos porque es una relación de muchos a muchos.
EDITAR
Si desea utilizarlo en muchos modelos, puede crear rasgos o crear otra clase base que amplíe Eloquent con el siguiente método:
public function attach($id, $relationship = null)
{
$relationship = $relationship ?: $this->relationship;
$this->{$relationship}()->attach($id);
$this->touch();
}
Ahora, si necesita esta funcionalidad solo necesita extenderse desde otra clase base (o usar rasgo), y ahora puede agregar a su clase de Product
una propiedad adicional:
private $relationship = ''assets'';
Ahora podrías usar:
Product::find($validId)->attach($anotherValidId);
o
Product::find($validId)->attach($anotherValidId, ''assets'');
si necesita adjuntar datos con la actualización del campo updated_at
. Lo mismo, por supuesto, debe repetir para separar.
Solución:
Cree un BaseModel
que amplíe Eloquent, haciendo un ajuste simple al método create
:
BaseModel.php :
class BaseModel extends Eloquent {
/**
* Save a new model and return the instance, passing along the
* $options array to specify the behavior of ''timestamps'' and ''touch''
*
* @param array $attributes
* @param array $options
* @return static
*/
public static function create(array $attributes, array $options = array())
{
$model = new static($attributes);
$model->save($options);
return $model;
}
}
Haga que sus modelos de Asset
y Product
(y otros, si lo desea) amplíen BaseModel
lugar de Eloquent
y configure el atributo $touches
:
Asset.php (y otros modelos):
class Asset extends BaseModel {
protected $touches = [ ''products'' ];
...
En sus sembradoras , establezca el segundo parámetro de create
en una matriz que especifique ''touch''
como false
:
Asset::create([...],[''touch'' => false])
Explicación:
El método save()
Eloquent acepta una matriz (opcional) de opciones, en la que puede especificar dos indicadores: ''timestamps''
y ''touch''
. Si touch
está configurado como false
, Eloquent no tocará los modelos relacionados, independientemente de los atributos $touches
que hayas especificado en tus modelos. Este es todo el comportamiento incorporado para el método save()
Eloquent.
El problema es que el método create()
Eloquent no acepta ninguna opción para pasar a save()
. Al extender Eloquent (con un BaseModel
) para aceptar el array $options
como el segundo atributo, y pasarlo a save()
, ahora puede usar esas dos opciones cuando llama a create()
en todos sus modelos que amplían BaseModel
.
Tenga en cuenta que el array $options
es opcional, por lo que al hacerlo no se romperán las demás llamadas para create()
que pueda tener en su código.