Working with Eloquent ORM

Using whereHas method

The whereHas operates on defined relationships in the model and allows for filtering rows based on queries of the relationship.

whereHas method accepts the name of the relationship (implemented by methods in model) as its first argument and an anonymous function as its second argument.

<aside> 💡

Anonymous Functions in PHP are function expressions that have no name and are often passed as arguments to other functions.

function a(f) { f() } // this function accepts an anonymous function as argument

a(function () { // anonymous function passed to function a
	echo 'this is an anonymous function';
});

Anonymous functions can be assigned to variables:

function a(f) { f() } // this function accepts an anonymous function as argument

$b = function () { // anonymous function assigned to variable b
	echo 'this is an anonymous function';
};

a($b); // anonymous function b passed to function a

If an anonymous function needs to access variables outside of its scope, it must create a closure over those by leveraging the use keyword:

$name = 'Laravel'

function a(f) { f() }

$b = function() use ($name) { // anonymous function b creating a closure
	echo 'Hello' . $name;
};

a($b);

</aside>

<aside> 💡

Anonymous Functions can be written with the arrow function syntax starting with PHP version 7.4. They automatically create a closure over variables of the previous scope and eliminate the need for use but are much more limited in their capabilities compared to regular anonymous functions.

$f = fn($n) => var_dump($n);

</aside>

<aside> 💡

Anonymous functions passed to whereHas method as the second argument, take a query builder instance as their argument, this query builder can be used to query the relationship referenced in the first argument.

</aside>

$users_with_active_phone_query = User::whereHas(
	'phone',
	fn($q) => $q->where('is_active', true) // sub query on phone relationship
);

Using get method

In order to run the queries and retrieve information from the database, get query must be invoked. By default, get method automatically selects all properties of the model it was used on

$users_with_active_phone = User::whereHas(
	'phone',
	fn($q) => $q->where('is_active', true)
)->get();

get method also accepts an optional array of property names select which columns must be selected in the database query. Properties of relationships are accessed by using dot notation in following format : relationship_name.property_name

<aside> 💡

In order to select all properties of a model or all columns of a table following notation is used: relationship_name.* or table_name.* . This normalizes the returned array and prevents creation of nested arrays.

</aside>

<aside> 💡

In order to rename a property when calling get the following notation can be used : property_name as new_property_name

</aside>

$users_with_active_phone = User::whereHas(
	'phone',
	fn($q) => $q->where('is_active', true)
)->get(['name', 'phone.number']); 

<aside> 💡

select method will also filter the columns returned by the query, however, unlike get it doesn’t invoke the query and must be followed with get down the chain. It doesn’t use an array for property names.

$users_with_active_phone = User::whereHas(
	'phone',
	fn($q) => $q->where('is_active', true)
)->select('name', 'phone.number')
->get(); // must be called 

</aside>

get method runs the query and returns the result as a collection

<aside> 💡

Collections are powerful, flexible wrappers built around arrays and allow for running eloquent queries on themselves.

$users = User::all(); // returned as collection
$verified_users = $users->where('is_verified', true); // query on collection

<aside> ⚠️

Loading all rows and running queries on collections is an anti-pattern and should be avoided. Always run the query directly for better optimization and performance:

$verified_users = User::where('is_verified', true); // correct

</aside>

</aside>

Using save method

save method is used to apply a change made on an object to the database. If not used after altering the object, the changes will not be made in the database.