with relaciones relacion hasmany example español belongsto php laravel eloquent

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.


Desde el origen del código , debe establecer $touch en falso al crear una nueva instancia del modelo relacionado:

Asset::create(array(),array(),false);

o usar:

$asset = new Asset; // ... $asset->setTouchedRelations([]); $asset->save();