firstOrNew with "or" statement - laravel

I have a table with columns like "name", "surname", "user_id", and I need to check if entry exists first by id, and then by name and surname together, if there is none, create it. How do I do it neatly, instead of making two update statements, and if both return 0 just create a new one (which seems too bulky)
I thought of using firstOrNew, but it seems that it only can work while matching all of the parameters.
Is there any method I've missed that would apply well to my situation?

You could try something like this (assuming you want to create a model [saved to the database]):
$attributes = [
'name' => ...,
'surname' => ...,
];
$model = Model::where('id', $id)
->orWhere(fn ($q) => $q->where($attributes))
->firstOr(fn () => Model::create($attributes));
This would search for a record by id OR name and surname. If it doesn't find one it will create a new record with the name and surname (assuming those attributes are fillable on the Model).
Laravel 8.x Docs - Eloquent - Retrieving Single Models / Aggregates firstOr

Related

How do I return the ID field in a related table in Laravel request

I have two related tables and I want to return all fields including the ID (key) field. My query below returns all fields except the ID. how do I return the ID field from one of the tables?
'programmes' => ProgrammeInstances::with('programmes')->get(),
the query below returns Unknown column 'programmes.programme_title' as it is looking for it in the table 'programme_instances'
'programmes' => ProgrammeInstances::with('programmes')->select('programmes.programme_title', 'programmeInstances.id', 'programmeInstances.name', 'programmeInstances.year')->get(),
Laravel provides multiple relationships, one of these is the hasMany() relationship which should return a collection where a User hasMany rows inside of your database
For example, inside your User model :
public function programmes() {
return $this->hasMany(Program::class);
}
Now in your controller, you can do :
public function edit($id) {
$programmes = User::find($id)->with('programmes')->get();
return view('user.edit')->with('programmes', $programmes);
}
And then you can loop over it inside your view
#forelse($programmes->programmes as $program)
// provide the data
#empty
// the user doesn’t have any programmes
#endforelse
a solution i found below - still not sure why ID isnt automatically returned when i get all fields, but works when i specify individual fields:
'programmes' => ProgrammeInstances::with('programmes')
->get()
->transform(fn ($prog) => [
'programme_title' => $prog->programmes->programme_title,
'id' => $prog->id,
'name' => $prog->name,
'year' => $prog->year,
]),

Laravel create multiple records in one query with Eloquent and get the created models

I know I can do the following to create multiple records with one query :
$users = [];
$users[] = ['name' => 'Tom'];
$users[] = ['name' => 'Jerry'];
$result = User::insert($users);
The problem of this approach is that ìnsert uses the query builder, and returns a boolean.
What if I wanted to have the created Eloquent models returned ? Would that be possible without having to do another query to retrieve them (how would I do that since I don't know the created ids ?) ?
I'm looking for something like the create method (which returns the created User model), but for multiple inserts (afaik createMany does not work outside of relationships)
Thanks

Laravel: Limit columns retrieve in an eagerly loaded relationship, with constraints

When I eagerly load a straightforward Eloquent relationship I can limit the columns retrieved using the following syntax:
MyModel::with(myRelation:id,col_2,col_3);
When I want to constrain the members of the relationship, I do this:
MyModel::with(['myRelation' => function ($query) {
$query->where([
['field_1', 'a_value'],
['field_2', 'b_value']
]);
}]);
But that loads all columns of the related models. I tried replacing the key myRelation with the full syntax, myRelation:id,col_2,col_3, but it throws an error which says the relation name is not found.
I also tried adding the following methods to the $query:
->select('id', 'col_2', 'col_3')
or
->addSelect('id', 'col_2', 'col_3')
or
->get('id', 'col_2', 'col_3')
None of these were successful.
One option could be keeping all the columns (except id, col_2, col_3) hidden in your original model MyRelation is pointing to.
protected $hidden = ['col_4', 'col_5',...];
You could also change the relationship definition in your model MyModel:
public function myRelation()
{
return $this->belongsTo('MyRelation')->select(array('id', 'col_2', 'col_3'));
}
But, in general, this should work:
MyModel::with(array('myRelation'=>function($query){
$query->select('id','col_2', 'col_3)
->where([
['field_1', 'a_value'],
['field_2', 'b_value']
]);
}))->get();
Remember, you have to include the joining key (say id) in the select list above.
Ok, so to get this to work, I had to:
include the foreign_key in the list of selects (thanks, #ankitPatel), and
pass the parameters to the select statement as an array, as follows:
->select(['id','foreign_key','col3','col4'])

Laravel, Can't update a soft-deleted value

I have this values in my db:
id - name - created_at - updated_at - deleted_at
------------------------------------------------
1 - John - 2018-11-11 - 2018-11-11 - (NULL)
2 - John - 2018-11-11 - 2018-11-11 - 2018-11-11
If I search for "John" with my Datatable (Yajra) I only see the John with id=1 because I'm using softdeletes. My model is this:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class MyModel extends Model
{
use SoftDeletes;
protected $fillable = ['name'];
protected $dates = ['deleted_at'];
}
When I delete (destroy) a registry it puts a date at deleted_at which is correct. But when I want to edit (update) John the Validator is giving me the error that that value is already in use. My update method is this:
public function update(Request $request, $id)
{
$rules = array(
'name' => 'unique:my_table'
);
$validator = Validator::make($request->all(), $rules);
if ($validator->passes()) {
MyModel::find($id)->update($request->input());
return redirect()->route('myroute')->withFlashSuccess('Ok!');
} else {
return redirect()->back()->withInput()->withErrors($validator);
}
}
What I'm doing wrong?
This person has written a blog post that looks like it would solve your problem:
https://wisdmlabs.com/blog/laravel-soft-delete-unique-validations/
I'm not 100% sure you want to do this though as you may later wish to use the restore() method to bring the soft-deleted data back. At which point, you'll have a collision.
The problem has nothing to do with the SoftDeletes, it is a validation issue. The unique validation rule is very special, because in case of an update it needs to know which entry it may ignore when performing the validation. In the background, the rule is performing an SQL query like
IF EXISTS (
SELECT id
FROM my_table
WHERE name = 'some value'
)
SELECT 1
ELSE
SELECT 0
(it may not be the exact query, but similar).
As you can see, the query does not take into account if you perform an update or not. Because your entity already exists, it will return 1 and therefore fail the validation because it thinks that the value under validation is not unique.
But there is actually a way to make the validation work for updates. You simply have to add the id of the existing entity (which is being validated) as third parameter to the validation rule. So your rules should look something like this:
$rules = [
'name' => 'unique:my_table,name,'.$id
];
Please be aware that there is also a second parameter for the unique validation rule - the column of the database table you want to search in.
Edit:
In case the unique constraint does only relate to not deleted entries, meaning that a unique value may be reused if other occurences of the same value are flagged as deleted, then it may be necessary to add an additional where() clause to the unique validation rule. For this, the fourth parameter needs to be set to the identifier column name and then we can add the additional where clauses as pairs of two parameters.
$rules = [
'name' => 'unique:my_table,name,'.$id.',id,deleted_at,NULL'
];
This will add where('deleted_at', 'NULL') (or whereNull('deleted_at')) to the query.

Return Eloquent model with different name from database tables

I want to return a JSON of an Eloquent model, but I'd like to change the array keys. By default they are set as the table field names, but I want to change them.
For example if I have a users table with two fields : id and user_name
When I return User::all(); I'll have a JSON with "[{"id" => 1, "user_name" => "bob}] etc.
I'd like to be able to change user_name to username. I haven't found the way to do it without an ugly foreach loop on the model.
I'm not sure why you would want to do this in the first place and would warn you first about the structure if your app/would it be better to make things uniform throughout.. but if you really want to do it.. you could do:
$user = User::find($id);
return Response::json(array('id' => $user->id, 'username' => $user->user_name));
That will return a JSON object with what you want.
You can also change the name of the key with:
$arr[$newkey] = $arr[$oldkey];
unset($arr[$oldkey]);
Just have a look at robclancy's presenter package, this ServiceProvider handles those things you want to achieve.
GITHUB LINK
Just set the $hidden static for you model to the keys you want to hide:
class User extends Eloquent
{
public static $hidden = 'id';
}
and name them the way you like with get and set functons.

Resources