How to make shortest code? - laravel

I use the following code to insert multi array to database:
foreach($request->category as $k => $v){
$category[] = array(
"category_id" => $v,
"announcement_id" => $announcement->id
);
}
AnnouncementCategory::insert($category);
So, input data is POST array $request->category.
I need to refactoring this code
I tried this code:
$announcement->categories()->attach($request->category);
In model Announcement I have:
public function categories()
{
return $this->hasMany("App\AnnouncementCategory", "announcement_id", "id");
}

If you define in your Announcement model relationship like this:
public function categories()
{
return $this->belongsToMany(AnnouncementCategory::class);
}
you can do it like this:
$announcement->categories()->attach($request->category);
EDIT
I see you updated your question and added categories relationship. But looking at your code, AnnounceCategory is rather pivot table, so you should use belongsToMany as I showed instead of hasMany

You can do it in one line if the request matches the columns:
AnnouncementCategory::insert($request->all());
Then in your AnnouncementCategory model, make sure you declare the protected $fillable array where you specify which field could be populated.

Related

Laravel 8 - I have an error in when using method in Model when using paratheses with the method

I have two models [The relation is one-many / Category-Trip]:
Category Model Code:
class Category extends Model
{
use HasFactory;
protected $fillable = [
'id',
'category_name',
'category_desc',
'category_img',
];
public function trip() {
return $this->hasMany(Trip::class);
}
}
Trip Model Code:
class Trip extends Model
{
use HasFactory;
protected $fillable = [
'name',
'description',
'max_visitors',
'price',
'date',
'image',
'guide_id',
'category_id',
];
public function category()
{
return $this->belongsTo(Category::class);
}
}
when I use this code, I will get this error:
TypeError Symfony\Component\HttpFoundation\Response::setContent():
Argument #1 ($content) must be of type ?string,
Illuminate\Database\Eloquent\Relations\BelongsTo given, called in
C:\Users\Orange\Desktop\ON
GITHUB\Tours-Booking\vendor\laravel\framework\src\Illuminate\Http\Response.php
on line 72
$trip = Trip::findOrFail(1);
return $trip->category();
But when write this without paratheses, i will not get this error, what is the problem?
$trip = Trip::findOrFail(1);
return $trip->category;
As a user explained (but not fully):
You are returning a relationship in your controller, but that is not valid (based on the error). Because BelongsTo cannot be serialized to a string, it tries to return the BelongsTo object, hence giving you that error (look, it is saying it is wanting null or a string, but you are returning BelongsTo).
When you do $model->relation (without ()), that means it will try to get all the data that satisfies the relation and store it as a Category in $model->relation (because it is a BelongsTo), but when you use $model->relation() you creating a query so you can query the relation with whatever you want/need, like $model->relation()->where('status', 'Active')->get()...
You have 2 solutions: either return $trip->category (it is going to be a Category object/model) or $trip->category()->get() or any query but use ->get() (will return a Collection) or ->first() (will return a Category model) at the end...
Read the documentation again so you understand better now: Relationships and First vs Get and BelongsTo documentation.
When you call the method its instantiating a eloquent instance in which you can extend it to a query which will give you a Category model
example (this might not be the most appropriate example)
$trip = Trip::findOrFail(1);
return $trip->category()->where('category_name', 'something')->first();
And the latter returns a collection.

how to define methods for a model that has a 1: 1 self relationship

I have this relational table on my db:
id, is referenced to: "attivitaSost" (and attivitaSpostata).
The relathionship is "optional" so the foreignkey is nullable.
But since the problem is the same, I will try to solve the first relationship first.
My model "cciActivities" have this 2 methods:
public function attOrig()
{
return $this->hasOne(CcieActivity::class,'id', 'attivitaSost');
}
public function attSpost(){
return $this->belongsTo(CcieActivity::class,'attivitaSost','id');
If I set the inverse:
public function attOrig()
{
return $this->hasOne(CcieActivity::class,'attivitaSost','id');
}
not works, and goes in a infinite loop thats goes in 500.
are well written? who needs to carry the foreign key? the children or the parent? there is a standard or I make work as was thinking:
save the new model,
pick up the id,
save it on the parent model,
The code:
$ccieActPadre= CcieActivity::where('id',$ccieActivityId)->first();
$ccieActivityNew = CcieActivity::create($data);
$ccieActPadre -> attivitaSost = $ccieActivityNew->id;
$ccieActPadre->save();
I am asking this, because when i try to apply methods filters like
$ccieActivities = CcieActivity::doesntHave('attOrig')
->get();
are returned not what i am expected.
When I am trying to render the resource activities, im using an api Resource like:
return [
'id' => $this->id,
'project' =>new ProjectResource($this->project) , //id, nomeEnte, name, email, ruolo
'catAttivita' => $this->catAttivita,
'nomeAttivita' => $this->nomeAttivita,
'descrizione' => $this->descrizione,
'dataInizioPrevista' => $this->dataInizioPrevista,
'dataFinePrevista'=> $this->dataFinePrevista,
'numNegoziAderentiPrevisti'=> $this->numNegoziAderentiPrevisti,
'numAziendeCoinvoltePreviste'=> $this->numAziendeCoinvoltePreviste,
'numInfluencerPartecipantiPrevisti'=> $this->numInfluencerPartecipantiPrevisti,
'numBuyerPrevistiB2B'=> $this->numBuyerPrevistiB2B,
'budgetTotalePrevisto'=> $this->budgetTotalePrevisto,
'modalitaRealizzazionePrevista'=> $this->modalitaRealizzazionePrevista,
'attivitaSpostata' => new CcieActivityResource($this->attOrigSpost),
'attivitaSostituitaaaaa' => new CcieActivityResource($this->attOrig),
];
this part
'attivitaSostituita' => new CcieActivityResource($this->attOrig),
never works! whatever method I apply!
So I need to understand which is the right convention to menage a 1:1 optional self relationship over a laravel model, thanks.
The second parameters for hasOne and belongsTo are not the same.
belongsTo is for the related model and hasOne is for the local model
$this->hasOne(Phone::class, 'foreign_key', 'local_key');
$this->belongsTo(User::class, 'foreign_key', 'owner_key');
In your case, the hasOne has the wrong parameters. change it to
public function attOrig()
{
return $this->hasOne(CcieActivity::class, 'attivitaSost', 'id');
}
EDIT:
Never eager load by default the parent in the child model and the child in the parent model even if they are seperate Classes. It will lead to an infinite loop.

Laravel - How to change response format for specific fields

I've "Product" model.
And need to change some value formats for only responses.
For example;
I've "price" on database as decimal (11,2).
I want this as "1.000.000,00" format on response.
Or created_at field to "Carbon::parse($this->created_at)->toDayDatetimeString()"
Or I want to add 3 specific columns with my user attribute, on response. (is_allowed etc.)
How can this be possible on model?
How can I response like that?
You can use Mutator and Accessor to set format :
https://laravel.com/docs/8.x/eloquent-mutators#accessors-and-mutators
public function setDateAttribute($date) {
$this->attributes['date'] = Carbon::createFromFormat('Y-m-d', $date);
}
public function getFirstNameAttribute($value)
{
return ucfirst($value);
}
As a best practice in Laravel you can use Eloquent Resources: Eloquent Resources
It's basically a "transformer" between models data and API/Responses Output.
The only one thing to notice is that in the Resource files yout must specify all fields and relations (if needed) of the Model manually.
In the toArray() function you can modify the type of all data of your model as you prefer.
If not, you can access the new field by $model->my_custom_field (Laravel can resolve the name of the getter function automatically).
public function toArray($request)
{
$editedFieldValue = doSomething();
return [
'my_field' => $editedFieldValue,
'other_field' => '',
];
}
If you want to do that in Model, you can create customs fields:
class MuModel extends Model
{
protected $appends = ['my_custom_field'];
public function getMyCustomFiledAttribute(){
$newData = doSomething($this->existent_field);
return $newData;
}
}
The $appends variable add the new fields to all responses generated from the Model, as a normal database field.
P.S.: You can create a getAttribute() function for existent database attribute and return the value as you want!
For example: getCreatedAtAttribute()

Fluently updating an existing one to one relationship with Eloquent in Laravel 5

I have a one-to-one relationship between my User model and an additional UserInformation model in which I store additional needed information which would bloat the "normal" user table.
I set up my models like this:
# User.php
public function information()
{
return $this->hasOne(UserInformation::class);
}
# UserInformation.php
public function user()
{
$this->belongsTo(User::class);
}
I have a profile page where the User can update information from both tables.
The view has inputs like this:
<input name="email"> // is a field in the users-table
<input name="information[size]"> // is a field in the users-information table
I read in different locations that I should be able to save both my User model and its relation in with:
$user->fill($request->all())->save();
But this throws the following error:
preg_replace(): Parameter mismatch, pattern is a string while replacement is an array
So my current solution looks like this:
auth()->user()
->fill($request->except('information'))
->save();
auth()->user()
->information
->fill($request->input('information'))
->save();
That works very good but doesn't look good in my opinion. So my question is: how can I clean that code up and save both in one go?
Have you tried including this
protected $guarded = array('information');
in your User.php model file
and then
auth()->user()
->fill($request->all())
->information->fill($request->input('information'))
->save();
I think your current solution looks fine, but if you want, you could always extract it out to your own custom method in your User model.
public function saveWithInformation($attributes)
{
$this->fill($attributes)->save();
$this->information->fill($attributes['information'])->save();
}
Then you can just call:
auth()->user()->saveWithInformation($request->all());
Since I was looking for a pretty flexible solution I came up with this function which I implemented into my User model (but it could also be included in a BaseModel)
public function fillWithRelation(array $request)
{
foreach ($request as $key => $value)
{
if (is_array($value) && method_exists($this, $key))
// check if the value is an array and if a method with the name of
// the key exists (which would be the relationship
{
$this->{$key}->fill($value);
unset($request[$key]);
}
}
return $this->fill($request);
}
This is definitely working if you include the information for a hasOne relationship like posted in my question.

Laravel ORM should return Relation

Is it possible to return an value for an hasOne relation directly with an Model?
For example:
$List = Element::orderBy('title')->get();
The Element has a "hasOne" Relation to an column:
public function type()
{
return $this->hasOne('App\Type', 'id', 'type_id');
}
How can i now return automatically the "type" for the Model?
At the Moment i am looping through all Elements, and build my own "Array" of Objects, including the Key of "type" in this example. But ill prefer to do this only in my Model.
Ill know how to add a "normal" property, but can it be someone from an relation?
public function getTypeAttribute($value)
{
return // how ?
}
protected $appends = array('type');
Is this possible?
Edit:
A workaround could be to use DB:: to return the correct value - but ill dont thing thats a good workaround: like:
public function getTypeAttribute($value)
{
// make a query with $this->type_id and return the value of the type_name
}
protected $appends = array('type');
you need to eager load your relations when getting the Element:
$list = Element::with('type')->orderBy('title')->get();
then access the type using
foreach ($list as $item) {
echo $item->type->type_name;
}
where type_name would be the name of a column in the types table
Make a query scope and in that scope, join your attributes from other tables.
http://laravel.com/docs/5.1/eloquent#query-scopes
http://laravel.com/docs/5.1/queries#joins

Resources