Laravel 5 - Limiting Eloquent Where Parameters When Using Request Object Data For Get - laravel

I have controller functions that look like:
public function get(Request $request){
return json_encode($this->users->get($request->all));
}
where $this->users references a repository for my User model. Inside of this repository, I have a function that looks like:
public function get($request){
return User::where($request)->get()->toArray();
}
I can dynamically pass in any number of parameters to search on through my HTTP request with the above code without having to explicitly state the column names.
This works well as long as all the parameters passed in through the request are valid column names. It will error out if I pass in a parameter that is a not valid table column.
I have the protected $fillable array defined in each of my models corresponding to my repositories.
Is there anyway to enforce what can be passed into the where() method above so that only valid columns defined in the $fillable array in my model are ever passed in?
For example, let's say I have a users table with columns (id, name, description). The GET URL I pass in looks like:
GET /user?name=mike&description=skinny&age=45
There are three parameters in the URL above, but only two of them are valid column names. I would like the age parameter to be automatically taken out. In my current code, the above URL will return an error response since the age column does not exist.

You can use array_only() helper:
return User::where(array_only($request, $this->fillable))->get()->toArray();
It will filter $request and will keep only key/value pairs which are in the $fillable array.
If this code is not in a model, change $this->fillable to appropriate variable or method call.

I wrote a package for doing this kind of filtering for models. It will allow you to filter based on User::filter($request->all())->get(); while defining each possible column's where() constraint.
https://github.com/Tucker-Eric/EloquentFilter

Try doing something like this:
public function get(Request $request)
{
$user = new User();
return User::where(
$request->only($user->getFillable())
)->get()->toArray();
}
By creating an instance of User you can then access it's fillable properties using getFillable(). By passing this list into $request->only(...) you can restrict the values you pull out to only those that are in your fillable array. Hopefully that should mean only fillable values are considered as part of your query.
Hope it helps.

Related

Repleace relationship by name of this relationship

I have a model User with fileds like - colorhair_id, nationality_id, etc. Of course I have a relationship to other model. Problem is that I want to return nationality from User i must do that:
User::find(1)->colorhair->name
In next time I need to use
User::find(1)->nationality->name
It works but it's not look professional and it's dirty. How can I change query or something else to return object with repleace field like nationality_id to nationality with name of that. Any idea?
You can use Laravel Mutators. Put below two functions into the User model
public function getHairColorNameAttribute(){
return $this->colorhair->name
}
public function getNationalityNameAttribute(){
return $this->nationality->name
}
Then when you simply access it.
User::find(1)->hair_color_name;
The next time
User::find(1)->nationality_name;
If you want to get these values by default use $append in the model. Put the following line to the top of the User model
protected $appends = ['hair_color_name','nationality_name'];
Note: In laravel 9 mutators little bit different from the above method.
Bonus Point :
if you access values in the same scopes don't use find() method in each statement.
$user = User::find(1);
then
$user->hair_color_name;
$user->nationality_name;

Laravel Eloquent getting data (with relationship) confusion

I am a bit confused on how to properly pull the data from Eloquent Models. I did read the documentation, but this is not mentioned anywhere or I missed it.
Let's assume we have two models, Client and Country (note: I only added relevant code, otherwise this question would be quite long):
Client:
<?php
namespace App\Models\Client;
use App\Models\BaseModel;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* #property int id
* #property int country_id
*/
class Client extends BaseModel
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
...
'country_id',
...
];
/**
* Return Country relationship
*
* #return BelongsTo
*/
public function country(): BelongsTo
{
return $this->belongsTo(Country::class);
}
}
Country:
<?php
namespace App\Models\Country;
use App\Models\BaseModel;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* #property int id
*/
class Country extends BaseModel
{
public function clients(): hasMany
{
return $this->hasMany(Client::class);
}
}
Nothing special, two normal Models. Now, when I try to pull the data I have a few issues:
dd(Country::findOrFail(32)); // CORRECT: returns selected country
dd(Country::findOrFail(32)->first()); // WRONG: returns first country in database, not the one with ID of 32, EXPECTED: single country object with selected country
dd(Country::findOrFail(32)->get()); // WRONG: returns all countries in database, EXPECTED: an array of objects with one country in it
dd(Country::with('clients')->findOrFail(32)->first()); // WRONG: returns first country in the database and not the one with ID of 32, relationship is also not correct, EXPECTED: object with selected country with valid relationship (clients) as array of objects
dd(Country::findOrFail(32)->clients()); // WRONG: returns HasMany relationship, no data, EXPECTED: an array of clients as objects
dd(Country::findOrFail(32)->clients()->get()); // CORRECT: returns valid data, array of clients as objects
dd(Country::findOrFail(32)->clients()->first()); // CORRECT: returns first client for selected country
So, why are all WRONG wrong ones? Am I interpreting the documentation wrongly? The most frustrating is dd(Country::with('clients')->findOrFail(32)->first()) since now I have no means on how to filter based on selected country and then also provide a list of clients in that country. I thought that Eloquent is quite advanced, so I assume that I am doing something wrong and would appreciate some guidance.
Please note that in latest example I also tried reversing query as dd(Country::findOrFail(32)->with('clients')->first()), same result.
In my opinion, you are mixing up a few things here. For example, combining FindOrFail() with first() is double. Look here, that's a good explanation I found:
find($id) takes an id and returns a single model. If no matching
model exist, it returns null.
findOrFail($id) takes an id and returns a single model. If no matching model exist, it throws an error1.
first() returns the first record found in the database. If no matching model exist, it returns null.
firstOrFail() returns the first record found in the database. If no matching model exist, it throws an error1.
get() returns a collection of models matching the query.
pluck($column) returns a collection of just the values in the given column. In previous versions of Laravel this method was called lists.
toArray() converts the model/collection into a simple PHP array.
Source: https://stackoverflow.com/a/33027466/14807111
The method description for the Builder method FindOrFail() is:
* Find a model by its primary key or throw an exception.
In your usecase this method is calling the find()method and executing:
$this->whereKey($id)->first($columns);
This will return the first model and by default all of its columns. Therefore any other first() afterwards will introduce another query.
Your first three "Wrongs" are because of execution order, the query executes the findOrFail first and afterwards it calls first() on the whole query again.
The fourth "WRONG" happens because your relationship returns a hasMany relationship, this is normal behaviour. You could either call ->get() afterwards or call ->clients without the () to receive the collection.
After all you are using the first() method wrong. You either use find(), findOrFail() or you use ->first(). But you should not use them together (there might be some edge cases where you have to, but genreally speaking, don't!).
Using find() or findOrFail() will already return the first() element that matched the ID.

How can I get the data inside my notifications from within my Controller?

I'm trying to get the numbers of records from my notifications, where the candidate_user_id column from inside the data attribute is the same as the UserId (Authenticated User).
After I dd, I was able to get the data from all of the records in the table by using the pluck method (Line 1). I then tried to use the Where clause to get the items that I need
but this just didn't work, it was still returning all of the records in the table.
DashboardController.php:
public function index()
{
$notifications = Notification::all()->pluck('data');
$notifications->where('candidate_user_id', Auth::user()->id);
dd($notifications);
}
Here is a partial screenshot of the data that is being plucked.
How can I get the data from this, in a way like this ->where('candidate_user_id', Auth::user()->id);?
If data was a JSON field on the table you could try to use a where condition to search the JSON using the -> operator:
Notification::where('data->candidate_user_id', Auth::id())->pluck('data');
Assuming you only want this data field and not the rest of the fields, you can call pluck on the builder directly. There isn't much reason to hydrate Model instances with all the fields to then just pluck a single field from them if it is just a table field, so you can ask the database for just the field you want.
The data in the data field is a json string, so you can tell Laravel to automatically cast it as an array using the $casts property on each of the models that is notifiable.
For instance, if you have a User model which uses the trait (ie has use Notifiable), add this:
protected $casts = [
'data' => 'array',
];
If you want to access all notifications for the auth user.
$user = auth()->user();
dd($user->notifications->pluck('data'));
If you really want to do in your question way, here is how.
$notifications = Notification::all()->pluck('data');
$notifications = $notifications->where('candidate_user_id', Auth::user()->id)
->all();
This assumes you that you did not modify the default laravel notifications relationship and database migration setup. If you have modified some of the default ones, you need to provide how you modify it.

Get specific values from controller function

I started learning Laravel and I am trying to achieve the following:
Get data from database and display specific field.
Here is my code in the controller:
public function show()
{
$students = DB::select('select * from students', [1]);
return $students;
}
Here is my route code:
Route::get('', "StudentController#show");
That all works for me and I get the following displayed:
[{"id":1,"firstname":"StudentFirstName","lastname":"StudentLastName"}]
How can I get only the "lastname" field displayed?
Thanks in advance!
DB::select('select * from students')
is a raw query that returns an array of stdClass objects, meaning you have to loop through the array and access properties:
$students[0]->lastname
You can also use the query builder to return a collection of objects:
$collection = DB::table('students')->get();
$student = $collection->first();
$student->lastname;
Lastly, using the query builder, you can use pluck or value to get just the last name. If you only have one user, you can use value to just get the first value of a field:
DB::table('students')->where('id', 1)->value('lastname');
I strongly advise you to read the Database section of the Laravel docs.
$students[0]['lastname'] will return the last name field, the [0] will get the first student in the array.
I would recommend creating a model for Students, which would make your controller something like this:
$student = Students::first(); // to get first student
$student->lastname; // get last names
If you only want the one column returned, you can use pluck()
public function show()
{
$last_names= DB::table('students')->pluck('lastname');
return $last_names;
}
This will return an array of all the students' lastname values.
If you want just one, you can access it with $last_names[0]
As a side note, your show() method usually takes a parameter to identify which student you want to show. This would most likely be the student's id.
There are several ways you can accomplish this task. Firstly, I advise you to use the model of your table (probably Students, in your case).
Thus, for example,to view this in the controller itself, you can do something like this using dd helper:
$student = Students::find(1);
dd($student->lastname);
or, using pluck method
$students = Students::all()->pluck('lastname');
foreach($students as $lastName) {
echo $lastName;
}
or, using selects
$students = DB::table('students')->select('lastname');
dd($students);
Anyway, what I want to say is that there are several ways of doing this, you just need to clarify if you want to debug the controller, display on the blade...
I hope this helps, regards!

Laravel, related model reuturened names

I have a model that has a related model
class A extends Model{
public function niceName()
{
return this->hasOne('App\NiceName2' ...);
}
In the controller when I retrieve data with submodel the result is like
a[nice_name_2] (using the table name) and I would like it to be a[NiceName2].
Is there a way to have an alias for the returned result? In cakePHP i know there is propertyName to set this on relations. Laravel has a similar feature?
Thanks
Laravel uses the convention of camelCase for method names and snake_case for attributes. I'm not sure there's an easy way around this.
When Laravel serializes the data, it converts relationships to snake_case, by convention. So NiceName2 would become nice_name2 when you execute toArray() or when the model is serialized (either in a JSON response or otherwise).
How this works is:
When you access $model->nice_name2 it converts the property name back to niceName2 to check for a relationship method with that name. When serializing, it converts the relationship niceName2 to the attribute name nice_name2.

Resources