I was introduced to Spatie\LaravelData when I was searching for how to implement DTOs in Laravel.
Using Data classes for insert/update for a single model is pretty straightforward. I tend to keep my updates atomic in this way.
However I have a problem for using LaravelData to retrieve objects and send to front end, specially with nested models.
I have this example I have Post and Category models:
class Post extends Model
{
use HasFactory;
protected $fillable = [
'title',
'category_id'
];
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
}
class Category extends Model
{
use HasFactory;
protected $fillable = [
'category',
'description'
];
}
I have these Data classes:
class PostData extends Data
{
public function __construct(
public string $title,
public string $category_id,
) {
}
}
class CategoryData extends Data
{
public function __construct(
public string $category,
public string $description
) {
}
}
I want to be able to send a STO object with Post and its Category, also would love to be able to have a collection of Posts and their Categories for index page.
I tried using this approach as described in their documentation, but first it gives an error and second I'm not sure how to return that from index() or view() methods in controller:
class PostData extends Data
{
public function __construct(
public string $title,
public string $category_id,
#[DataCollectionOf(CategoryData::class)]
public DataCollection $category,
) {
}
}
Anyone ever used Spatie\LaravelData for returning DTOs to from end?
You are trying to use a DataCollection when you should be using nesting. This is because your post has a BelongsTo relationship with the category model.
class PostData extends Data
{
public function __construct(
public string $title,
public string $category_id,
public CategoryData $category,
) {
}
}
Related
before so sorry for my bad english. Whats mean that error? "Call to a member function where() on null". In my code need obtain all application for a each category.
Im interesting in use a Model route binding, its works well when I insert a one model, for example: Application. Example:
Route::get(applications/{application},'ApplicationController#show')->name('apps'));
But when I try use 2 model in a route, have this problem. Here my code:
Route::get('categories/{category}/applications/{application:category_id}','ApplicationController#show')->name('apps');
My models:
class Application extends Model{
protected $fillable = ['name', 'price', 'category_id', 'vote', 'image_src'];
public function users()
{
return $this->belongsToMany(User::class, 'applications_users_states', 'application_id', 'user_id')
->withPivot('state_id')
->withTimestamps();
}
public function categories()
{
return $this->belongsTo(Category::class);
}
public function logs()
{
return $this->belongsToMany(Log::class)->withTimestamps();
}
}
class Category extends Model
{
protected $fillable = ['name', 'description'];
public function applications()
{
$this->hasMany(Application::class);
}
}
My Controller:
public function show(Category $category,Application $application)
{
return $application;
}
Thanks for your time and for your knowledge!!!!!
I'm quite new to Laravel and now I'm trying to move parts of a former application from a small self written framework into Laravel. The address book is multilingual therefor the table structure is a little more complicated.
db-structure
And this is my source code:
AddressBookController.php
namespace App\Http\Controllers;
use App\AddressBook as AB;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class AddressBookController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$entries = AB::all();
return view('addressBook')->with([
'class' => __CLASS__,
'function' => __FUNCTION__,
'line' => __LINE__,
'entries' => $entries,
]);
}
}
Model AddressBook.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class AddressBook extends Model
{
protected $table = 'address';
protected $primaryKey = 'address_id';
protected $keyType = 'int';
public $incrementing = true;
public $timestamps = false;
protected $searchable = [
'columns' => [
'address.address_surname' => 10,
'address.address_company' => 5,
'address.address_vatid' => 2,
],
];
public function country() {
return $this->hasOne('country', 'country_id', 'country_id');
}
public function addresstype() {
return $this->hasOne('addresstype', 'addresstype_id', 'addresstype_id');
}
}
Model Country.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Country extends Model
{
protected $table = 'country';
protected $primaryKey = 'country_id';
protected $keyType = 'int';
public $incrementing = true;
public $timestamps = false;
public function translation() {
return $this->hasOne('translations', 'translations_id', 'translations_id');
}
public function addressbook() {
return $this->belongsTo('address', 'country_id', 'country_id');
}
}
Model AddressType
namespace App;
use Illuminate\Database\Eloquent\Model;
class AddressType extends Model
{
protected $table = 'addresstype';
protected $primaryKey = 'addresstype_id';
protected $keyType = 'int';
public $incrementing = true;
public $timestamps = false;
public function translation() {
return $this->hasOne('translations', 'translations_id', 'translations_id');
}
public function addressbook() {
return $this->belongsTo('address', 'addresstype_id', 'addresstype_id');
}
}
Model Translation.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Translation extends Model
{
protected $table = 'translations';
protected $primaryKey = 'translations_id';
protected $keyType = 'int';
public $incrementing = true;
public $timestamps = false;
public function country() {
return $this->belongsTo('country', 'translations_id', 'translations_id');
}
public function addresstype() {
return $this->belongsTo('addresstype', 'translations_id', 'translations_id');
}
}
The request "$entries = AB::all();" works in general but I get the id's and maybe I'm completely wrong here but I thought that data from foreign keys will be replaced with the respective models (if configured correctly). so my question is:
a. did I make a mistake during configuration and if yes, where exactly is the error?
or
b. is my assumption of replacing id's with objects is completly wrong?
Thanks in advance!
Steve
Laravel Eloquent models do not replace the foreign keys of the active records with the relational data, it only appends a new property with the same name as the method relating the classes and in that property it puts all the Model instances of the resulted query, this ONLY if you access the property, its called Eager Loading.
It is explained here (Ofiicial Documentation)
$addressBook = App\AddressBook::find(1); //this only will return the active record with the id 1 and nothig more.
$addressBook->country; // The property "country" does not exist in the AddressBook Classs but eloquent models will return a "fake" property with the value of the result of the query by the method with the same name (only if the method returns an Eloquent Relation).
This Eloquent behavior is natural, and is a very clever way to minimize the number of queries, Eloquent will never load a relation if there is no need to.
If you want to load a relation in a set the models at the same time that those models are retriving from the db you need to explicitly especify wich relations you want to load.
$addressBook = App\AddressBook::with(['country', 'addresstype', 'anotherRelation'])->get(); // this will retrive all the addressBook models and in each one will attach the relations specified.
[EDITED]
Also you have to place the entire namespace of the related model class in the relation methods so you need to replaced like:
class Translation extends Model
{
protected $table = 'translations';
protected $primaryKey = 'translations_id';
protected $keyType = 'int';
public $incrementing = true;
public $timestamps = false;
// ****** You need to put the entire namespace of the Model class
public function country() {
return $this->belongsTo('App\Country', 'translations_id', 'translations_id');
}
We have the following class using $with:
class CargaHorasEmpleado extends Model
{
protected $table = "empleados_horas";
protected $with = ["tipoTarea", "proyecto", "empleado", "empleadoQueHizoLaCarga"];
public function tipoTarea()
{
return $this->belongsTo('App\TipoTarea', 'id_tipo_tarea', 'id')->withTrashed();
}
public function empleado()
{
return $this->belongsTo('App\Empleado', 'id_empleado', 'id')->withTrashed();
}
public function empleadoQueHizoLaCarga()
{
return $this->belongsTo('App\Empleado', 'id_empleado_cargo_hs', 'id')->withTrashed();
}
public function proyecto()
{
return $this->belongsTo('App\Proyecto', 'id_proyecto', 'id')->withTrashed();
}
}
This is the class TipoTarea
namespace App;
use Illuminate\Database\Eloquent\Model;
class TipoTarea extends Model
{
protected $table = 'tipos_tareas';
public $timestamps = false;
protected $fillable = [
'titulo', 'descripcion'
];
}
Thep page throws the error: "Call to undefined relationship [tipoTarea] on model [App\CargaHorasEmpleado]". That's the only relationship that's not working. The others are fine. What's wrong?
Well, isn't the relationship called "tipoTarea"? You wrote "tiposTarea"
The problem was that my class "TipoTarea" didn't use softdeletes. So the error was in using the "WithTrashed" method. The correct way is:
public function tipoTarea()
{
return $this->belongsTo('App\TipoTarea', 'id_tipo_tarea', 'id');
}
This is my model:
class Positions extends Model implements Repository
{
protected $fillable = ['index_id', 'title', 'description'];
public function index()
{
return $this->belongsTo('TEST\Indices', 'index_id');
}
public function getById($id)
{
return $this->with('index')->find($id);
}
}
how to use pluck() in getById() function for listing index relationship?
You can do it like this :
return $this->with('index')->find($id)->pluck('index.indexField');
i am sorry, if my question not make sense...
i have these two tables Area, and Address and are related via foreign key... here is the area and address models
class Area extends Model
{
protected $table='areas';
public function sections(){
$this->hasMany(Area::class,'area','id');
}
public function address(){
$this->belongsTo(Address::class,'id');
}
}
class Address extends Model
{
protected $table='address';
public function area(){
return $this->hasMany(Area::class,'id');
}
}
and i have a third table section which is connected to area table.
my question can i make eloquent relation that can access the data of address table from the section "section table connect with area not address"
class Section extends Model
{
protected $table ='sections';
protected $fillable =[
'sec_code',
'area',
'id',
];
public function beneficiaries(){
return $this->hasMany(Beneficiary::class,'ben_sec','id');
}
public function area_sec(){
return $this->belongsTo(Area::class,'area');
}
public function address(){
// need the eloquent relation if could
}
}
thank you
Call it as a property not a method:
$model->area_sec;
You can get the data with access it from your Area relation first, so the code will be like this:
class Section extends Model
{
protected $table ='sections';
protected $fillable =[
'sec_code',
'area',
'id',
];
public function area_sec(){
return $this->belongsTo(Area::class,'area');
}
public function address(){
return $this->area_sec->address;
}
}