How’s it going? Greetings from Argentina!
I’d like to share an issue I encountered while working on a Laravel project of mine, intended for property rentals. In the database, both the property owner’s and the tenant’s contact information are stored in the same table.
The MySQL 'datos_contacto' (contact_data) table looks like this:
id tipo_usuario email horario_atencion telefono telefono_alt celular whatsapp alquiler_id
---------------------------------------------------------------------------------------------------------------------------------------
42 contratador [email protected] 24x7 11-3352-6425 4235-5532 11-3352-6425 (54) 11-3352-6425 31
43 publicador [email protected] 24x7 11-3546-5888 4701-1108 11-3546-5888 (54) 11-3546-5888 31
Now, in the Eloquent model that represents a rental property record, I’ve added an attribute that displays the contact information for both parties.
Currently, although the model behaves as expected by the application, when inspecting a rental instance, the datosContacto attribute is displayed like this:
leandro@Leandro:~/Proyectos/infoalquiler$ php artisan tinker
Psy Shell v0.12.12 (PHP 8.4.11 — cli) by Justin Hileman
> use App\Models\Alquiler;
> $alquiler=Alquiler::find(31);
= App\Models\Alquiler {#6018
(....)
datosContacto: App\Models\DatosContacto {#6726
#id: 42,
#tipo_usuario: "contratador",
#email: "[email protected]",
#horario_atencion: "24x7",
#telefono: "11-3352-6425",
#telefono_alt: "4235-5532",
#celular: "11-3352-6425",
#whatsapp: "(54) 11-3352-6425",
#alquiler_id: 31,
+contratador: App\Models\DatosContacto {#7043
id: 42,
tipo_usuario: "contratador",
email: "[email protected]",
horario_atencion: "24x7",
telefono: "11-3352-6425",
telefono_alt: "4235-5532",
celular: "11-3352-6425",
whatsapp: "(54) 11-3352-6425",
alquiler_id: 31,
},
+publicador: App\Models\DatosContacto {#6875
id: 43,
tipo_usuario: "publicador",
email: "[email protected]",
horario_atencion: "24x7",
telefono: "11-3546-5888",
telefono_alt: "4701-1108",
celular: "11-3546-5888",
whatsapp: "(54) 11-3546-5888",
alquiler_id: 31,
},
},
In English it would be like:
contactData: App\Models\ContactData {#6726
#id: 42,
#user_type: "contractor",
#email: "[email protected]",
#support_hours: "24x7",
#phone: "11-3352-6425",
#alt_phone: "4235-5532",
#mobile: "11-3352-6425",
#whatsapp: "(54) 11-3352-6425",
#rental_id: 31,
+contractor: App\Models\ContactData {#7043
id: 42,
user_type: "contractor",
email: "[email protected]",
support_hours: "24x7",
phone: "11-3352-6425",
alt_phone: "4235-5532",
mobile: "11-3352-6425",
whatsapp: "(54) 11-3352-6425",
rental_id: 31,
},
+publisher: App\Models\ContactData {#6875
id: 43,
user_type: "publisher",
email: "[email protected]",
support_hours: "24x7",
phone: "11-3546-5888",
alt_phone: "4701-1108",
mobile: "11-3546-5888",
whatsapp: "(54) 11-3546-5888",
rental_id: 31,
},
},
Even though it works, it includes unwanted redundant information, and perhaps the model code is not the most optimal.
Ideally, I would like it to look like this:
datosContacto: App\Models\DatosContacto {#6726
+contratador: App\Models\DatosContacto {#7043
id: 42,
tipo_usuario: "contratador",
email: "[email protected]",
horario_atencion: "24x7",
telefono: "11-3352-6425",
telefono_alt: "4235-5532",
celular: "11-3352-6425",
whatsapp: "(54) 11-3352-6425",
alquiler_id: 31,
},
+publicador: App\Models\DatosContacto {#6875
id: 43,
tipo_usuario: "publicador",
email: "[email protected]",
horario_atencion: "24x7",
telefono: "11-3546-5888",
telefono_alt: "4701-1108",
celular: "11-3546-5888",
whatsapp: "(54) 11-3546-5888",
alquiler_id: 31,
},
},
Here's the code for my DatosContacto.php model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Models\Alquiler;
use Illuminate\Database\Eloquent\Casts\Attribute;
class DatosContacto extends Model {
use HasFactory;
protected $guarded = [];
public $timestamps = false;
protected $table = 'datos_contacto';
protected $appends = ['contratador', 'publicador'];
protected $hidden = ['id','alquiler','tipo_usuario','email','horario_atencion',
'telefono','telefono_alt','celular','whatsapp','alquiler_id'];
public function alquiler() {
return $this->belongsTo(Alquiler::class, 'alquiler_id');
}
protected function contratador(): Attribute {
return new Attribute(
get: function ($value) {
$contratador = Self::where(['tipo_usuario' => 'contratador', 'alquiler_id' => $this->alquiler->id])->get()->first();
if ($contratador) $contratador->setHidden(['alquiler'])->setAppends([]);
return $contratador;
}
);
}
protected function publicador(): Attribute {
return new Attribute(
get: function ($value) {
$publicador = Self::where(['tipo_usuario' => 'publicador', 'alquiler_id' => $this->alquiler->id])->get()->first();
if ($publicador) $publicador->setHidden(['alquiler'])->setAppends([]);
return $publicador;
}
);
}
}
Here's the code for my Alquiler.php model:
<?php
namespace App\Models;
//(...)
use App\Models\DatosContacto;
class Alquiler extends Model {
use HasFactory;
use HasSpatial;
public function getRouteKeyName() {
return 'slug';
}
//Habilitamos la asignación masiva:
//Podríamos hacerlo mediante $fillable -> protected $fillable = ['barrio','tipo','direccion','descripcion'];
//Pero nos conviene hacerlo mediante $guarded:
protected $guarded = [];
protected $casts = ['disponible' => 'boolean'];
//protected $hidden = ['coordenadas'];
protected $appends = ['reputacion','ambientes'];
protected $with = ['direccion','caracteristicas','fotos','servicios','aviso','datosContacto'];//'ambientes'
protected $table = 'alquileres';
private $obj = null;
public function recibirObj($obj) {
$this->obj = $obj;
return $this;
}
public function resetear() {
$this->obj = null;
return $this;
}
//Relaciones a nivel de modelo
//Asocio el campo 'user_id' (FK de esta tabla) con el campo 'id' (PK) de la tabla 'users'
public function publicador() {
return $this->belongsTo(User::class, 'user_id');
}
//Asocio el campo 'contratador_id' (FK de esta tabla) con el campo 'id' (PK) de la tabla 'users'
public function contratador() {
return $this->belongsTo(User::class, 'contratador_id');
}
public function direccion() {
return $this->hasOne(Direccion::class, 'alquiler_id');
}
public function datosContacto() {
return $this->hasOne(DatosContacto::class, 'alquiler_id');
}
public function multimedia() {
return $this->hasOne(Multimedia::class, 'alquiler_id');
}
public function solicitudes() {
return $this->hasMany(Solicitud::class, 'alquiler_id');
}
public function caracteristicas() {
return $this->hasOne(Caracteristicas::class, 'alquiler_id');
}
public function aviso() {
return $this->hasOne(Aviso::class, 'alquiler_id');
}
public function servicios() {
return $this->belongsToMany(Servicio::class);
}
public function fotos() {
return $this->hasMany(Foto::class, 'alquiler_id');
}
public function calificaciones() {
return $this->morphMany(Calificacion::class, 'calificacionable');
}
public function ambientes() {
return $this->hasMany(Ambiente::class, 'alquiler_id');
}
//(...)
}
Do you have any suggestions to improve or optimize this code?
While it works, I’m not sure it’s the best approach to achieve this.
If there's any more information needed, please tell me and i'll be sharing fast as posible.
Thanks, and best regards!
Leandro
P.S.: Since I'm a pretty newbie user, I'm still unable to upload screenshot, which would help a lot to see the issue more clearly