I inherited a Laravel project and I'm still working my way through learning it. I see in the code there is a check to run a block only if the $patient->records is there but check for it is:
if(! empty($patient->records) && $patient->records->count() > 0)
is that redundant or is empty and count() checking 2 separate things?
EDIT: corrected from > 1 to > 0
I think it's redundant. $patient->records is a collection.
I use like this.
if($patient->records->count())
{
// has records
}
If there is a possibility that $patient or records (assumed to be a property of $patient) can be undefined, then the empty($patient->records) is helpful because $patient->records->count() by itself would result in a fatal error:
Uncaught Error: Call to a member function count() on null in ...
With empty($patient->records) in the if clause, that fatal error can be avoided, even in the case that $patient or records is undefined.
The empty is checking that $patient->records is defined and not empty, while the count() part of the clause is a more specific check on the result of the count method. That method is not available in the case where $patient or records is undefined.
Yes, it is redundant.
Then, why someone can possible have written the code this way?
May be, before checking the length of the collection he wanted to make sure that it's not null though I prefer using is_null() or isset() method instead in such case. Even if that(null safe) was in his mind, still it's unnecessary. Because if the relationship between patient and records defined correctly(one to many => hasMany()) then $patient->records will always return a collection but never null. Even if it's an empty collection you can still use count() method safely without even checking if the records is null or not because it's never going to be null if we return a hasMany() relation.
What could be a better solution
You can just use this same condition removing the empty checking part as below:
if($patient->records->count() > 0)
To make it more readable, Laravel collection is offering us a very nice method called isNotEmpty():
if($patient->records->isNotEmpty())
This is what I prefer to use. Because we write code to make it readable to other programmer/us, not for computer. Computer can even understand binary but we don't write binary.
Related
After working with Nodejs, i got used to various promise-based api's that would throw an exception/error in case of some error.
In both Codeigniter and Laravel, i see that the ALL examples of querying sort of "assume" that everything "will be ok": no try-catch or any other form of error handling.
For instance, i see that CI's "insert" method returns true on success, false on failure. Despite that, none of the examples i've seen, uses any if/else Boolean check on the operation. Same goes for Laravel, which is obviously a much more modern and professional framework.
What i did for now, is to manually check for "true" or "false", for every query that returns a boolean, but the problem is some methods return other types, like the "insert_batch" methods, whose return type is stated as "mixed".
Can someone shed a light on this issue? What approach do others take, to make sure nothing breaks, due to poor database error handling?
A link for the relevant section in CI's docs:
https://www.codeigniter.com/user_guide/database/query_builder.html?highlight=query%20builder
The "read" queries - get() and get_where() - are not documented correctly. It says the return is a CI_DB_result but it can also return FALSE. I always check the return of the "read" methods using this pattern (or something like it).
$query = $this->db->get();
return $query !== FALSE ? $query->result() : NULL;
The main thing is to know that $query isn't FALSE before trying to run a method on it.
For "write" queries simply returning the result of the Query Builder method works, e.g.
return $this->db->insert('mytable', $data);
Naturally, the receiver of this return needs to pay attention.
The next line of code could return any one of the following: TRUE, FALSE, or a CI_DB_result object.
$this->db->query('YOUR QUERY HERE');
The full definition of query() is
DB_driver::query($sql, $binds = FALSE, $return_object = NULL);
Adjust your return checking to the $sql string request.
Once you nail the query statement syntax down future problems are most often related to bad input data. I usually spend more time checking query input data than output. Things like: Is there actually a value here? Is it the correct data type? Is it in the expected range? Garbage in - Garbage out.
In the code below, $request->input('id') returns an integer. I can return just that and see it every time. Or set it to a variable and return that to see it every time also. If I replace the $request->input('id') in my code with just that integer value I get what I'm looking for, however if I leave in a variable it always returns an empty set. What is going on? I'm so confused!
public function delete($id, Request $request)
{
$community = Community::find($id);
return $community->categories->where('id', $request->input('id'))->first();
}
When you do $community->categories->where(... you are calling the where method on a Collection object, which is the result of the relationship query. So, you are running both where and first with all related categories in memory, after fetching them from the database.
If you did $community->categories()->where(... you are calling the where method on a Relation object (which itself contains a Builder). In this case, the query is not triggered until you run the last method first (and most often get), and also, the where filtering is done by the DBMS, your application only receives the matching categories. This may be more efficient.
So, to the point of your question:
You may notice that the signature of the Collection::where method has a third parameter which is whether the comparison is strict, which defaults to true. My guess is the request input is coming as a string, and doesn't strictly match the id which is cast as int.
Meanwhile, the Builder::where method doesn't have strict comparison (again, it's not PHP who execute it). So you have no worries there.
Option 1:
return $community->categories->whereLoose('id', $request->input('id'))->first();
Option 2 (better, in my opinion):
return $community->categories()->where('id', $request->input('id'))->first();
I have a method like this:
public function create (array $hash) {
$id = $hash[ID_KEY];
$this->store[$id] = $hash;
}
I want to guard it against bugs caused by bad input.
For instance, my code can mistakenly pass $hash with
$id = '' or $id = null,
in which case it will be stored silently referenced by null. Instead I want to see a warning and revise my code to get rid of it. So I guess the best way is to throw Exception:
if (! $id) throw new Exception("Hash with empty id");
Note that I am using empty strings as default values for several method arguments and for default return values, so this kind of bug can easily occur. (Using null instead of empty string here doesn't seem to change anything, even if it is not recommended by Uncle Bob.)
The problem is -- there are many methods like that. Is it really best practice to guard each of them for each argument that can become null but shouldn't?
For instance, another method does only reading. Then it seems there is no need to guard against null because nothing will ever be stored referenced by null, right? Or shall I still guard defensively, to prepare for the case I may somewhere in the future decide to allow storage referenced by null and forget to adjust the guards?
This sounds kind of the safest way but would clutter all methods with bulks of guarding code for all indices involved there. Is this really the best way?
EDIT.
I put more guards and indeed discovered few bugs that I wouldn't find otherwise. Also my tests didn't spot them.
Furthermore, it helped to better understand the role of read methods - to return value if found or return empty Array if not. The input $id = null goes under not found and hence also returns empty Array. That way the method is clean and consistent.
You can use PHP's is_null() and empty() to easily manage this kind of output.
Also I suggest you to write a list of function used only in debug (since you want to perfectionate your code). Call these function in each method you want to test and set a constant like DEBUG_MODE to handle the debug functions' behavior. All of this could be also done using unit testing, which would require more attention and time. But if you have both or want to learn something new, unit testing is clearly a better choice.
Also it is a good practice to handle ALL CASES you can think of. For instance, if your "reading method" expects not to find a null value (because you think there are none because you eradicated by testing over testing), if this "reading method" happens to find a null value a "ugly" PHP error would be shown somewhere, or worse, if error_report is shut you might never see a problem, or even worse, the code might continue its execution and utterly damage further data.
I've seen tons of threads about what to return in case a PHP function fails. But they were all about PHP 4.
Now in my most recent (PHP 5) project, I want to enforce some sort of consistency with return types (I don't even know, if it's gonna be worth it down the road). So if the normal return type of a method is an array, what should I return in case the method fails?
null
empty array()
throw an exception instead
In C# I would return null, so should I write PHP constantly thinking what I would do in a strongly typed language? Or does something make more sense in this specific scenario? What are the pros and cons for each of the options?
If there was no error in performing the logic of your method (in which case I would suggest throwing an exception), I would say return an empty array. It would make an situation like this easier to deal with:
foreach($obj->method() as $item)
{
// ...
}
If YourClass::method() returned null, I would get an "Invalid argument supplied" warning, whereas returning an empty array would never trigger that.
Whatever you end up choosing, stick with it. No one likes working with an API whose return values are inconsistent, and have no logical meaning.
I would throw an exception on an unrecoverable error.
For example, let's say you have a method like getById($id). It is ok to return null if nothing is found. Now let's say you have another method, delete($id) that will itself call getById($id). In this case, if nothing is found (i.e: getById returned null) an exception should be thrown.
Remember that whenever you report an error with a return value, being a null, empty array or string, or even a integer, you will need to handle this error, probably doing some cleanup somewhere else depending on the returned value. This might lead to some dirty in all the methods/functions involved.
Using exceptions will allow you to handle and catch these errors at one place only (at the catch). So you dont have to replicate the code that checks for the error (was the return an empty array? was the return value 1, 2, or 9?, etc). Besides that, using exceptions lets you easily "catalog" the kind of error (like a business logic exception, or an invalid arguments) without writing a lot of if's along the source.
So if a function/method returns something (i.e: it terminated normally), the return value is something you can use (an empty array, a null, or empty string, are all valid return values). If a function/method throws an exception, clearly something went wrong.
Another important thing is that null can be equals to false, 0, and an empty string if you dont use strict checking (=== vs. ==) so this might lead to other kind of bugs. This is where exceptions can also be an advantage.
And of course, whatever you decide, the important thing is to be consistent with your choices along the code.
If a function really 'fails', then I would go for an exception. Exceptions are meant to indicate a failure.
Still, at some point you need to decide what to do with the exception, and then it might very well be that you decide that returning an empty array might be the best. That would for example be useful if you want to keep your existing foreach() loops working.
Thus, use exceptions, but think about where you want to catch them and process them into something useful for the user.
If the method fails it should be classified as an exception IMO this will allow you to properly catch the expected expections and act upon them.
I'm a strong believer in normalizing responses from functions and methods. It makes life a lot easier when you know what the function will return.
In short if it fails throw and exception if it doesn't ensure an array is returned, my $0.02
I know why I am getting this error, and this is because there currenly no ratings for this specific dish, so it is null or empty but I am not sure how to handle it.. I tried this but still the same error came up..
if(empty($rate_dishes[$j]["DishRating"]["dish_id"]))
{
echo $this->Form->hidden('dish_id', array('value'=> $this->params['var']));
}
else
{
echo $this->Form->hidden('dish_id', array('value'=> $rate_dishes[$j]["DishRating"]["dish_id"]));
}
Basically I am providing the dish id if its not provided by the posted array.. Whenever there is a rating for that particular dish it takes the 'else' condition.. but if it does not have a rating.. it gets to the first condition and still shows that error..
the URL would be /special_dishes/rate_dishes/4/?var=4
You probably shouldn't check for empty indices in that case, but rather if the array key exists. This is not really a feature of CakePHP, but rather just a standard PHP method.
The PHP function for this is isset() rather than empty(), as the latter expects the key to be there:
isset($rate_dishes[$j]["DishRating"]["dish_id"])
Also, there's array_key_exists(), but I believe you're looping through all the data in that post array, so using isset() is probably just fine.