I've a method that returns a Doctrine_Collection, with a whereIn() clause :
public function getByValues($values)
{
if (!is_array($values))
throw new sfException('Wrong parameter type. Excepted array.');
return Doctrine_Query::create()
->from('Anomaly a')
->whereIn('a.value', $values);
}
However, when $values is an empty array, this method return all the rows that are in the AnomalyTable. This isn't an unexpected behavior, as documented in Doctrine documentation, and written here : Doctrine where in with Doctrine_Query
However, I would like to return an empty Doctrine_Collection instead of the result of my query, when $values is an empty array.
Any ideas on how I can do that ?
Thanks =)
Edit:
Adding an impossible clause, like ->where('1=0') would do the trick, but it is an unnecessary request to the DB server. Does anyone have a better idea ?
And what about (you need also the execute method to get the result of the query ! with that the return type will be the same everytime) :
public function getByValues($values)
{
if (!is_array($values))
throw new sfException('Wrong parameter type. Excepted array.');
if (empty($values)) {
return new Doctrine_Collection('Anomaly');
}
return Doctrine_Query::create()
->from('Anomaly a')
->whereIn('a.value', $values)
->execute()
;
}
By value, I assume you mean $values right? just add something to check if values is empty and then manually supply an empty collection.
if(empty($values))
return Doctine_Query::create()->from('Anomaly a')->where('1=0');
if(count($values)){
return Doctrine_Query::create()
->from('Anomaly a')
->whereIn('a.value', $values);
} else {
return Doctine_Query::create()
->from('Anomaly a')
->where('0=1');
}
I think it's impossible to do that.
The Doctrine_Collection is more than a resultset/array of objects. It also has means of deleting and adding objects and keeping that state.
That's probably also why many native Doctrine functions return FALSE when no results were found. (For instance, the NestedSet functions).
So, for you it's probably best to return FALSE as well. or maybe an empty array. Both arrays as Doctrine_Collection can be used in a foreach loop and count function.
Should you want to use the delete and add functions, you could just call the constructor.
I personnaly use the following trick:
public function getByValues($values)
{
if (!is_array($values))
throw new sfException('Wrong parameter type. Excepted array.');
$values = empty($values) ? array(-1) : $values;
return Doctrine_Query::create()
->from('Anomaly a')
->whereIn('a.value', $values);
}
Works great even if somewhat hackish.
Related
I was trying to get the remaining time for movie delivery in a rental system made with laravel.
I built the following query using eloquent and it returned the result I wanted:
$resting_time = DB::table('alquiler')
->join('socio','alquiler.soc_id','=','socio.id')
->join('pelicula','alquiler.pel_id','=','pelicula.id')
->select('socio.soc_nombre','pelicula.pel_nombre','alquiler.created_at', DB::raw("DATEDIFF(alq_fecha_hasta,NOW()) AS Days"))
->orderBy('Days','asc')
->paginate(6);
but there is a problem when these rentals go over the delivery deadline it returns negative values, so I would like the query to return only the rentals that have the remaining days greater than zero and then paginate those results.
I create this statement and using map() filter only the positives that are returned in a collection but the problem is that I can't paginate them.
$resting_time = DB::table('alquiler')
->join('socio','alquiler.soc_id','=','socio.id')
->join('pelicula','alquiler.pel_id','=','pelicula.id')
->select('socio.soc_nombre','pelicula.pel_nombre','alquiler.created_at', DB::raw("DATEDIFF(alq_fecha_hasta,NOW()) AS Days"))
->get()->map(function($alquiler){
return ($alquiler->Days >= 0) ? $alquiler : null;
});
$resting_time = $resting_time->filter()->sortBy('Days');
This is the returning collection:
But this type of collection cannot be paginated.
Any idea how to fix it, or maybe an easier way to do it? Sorry if something doesn't make sense, I'm just starting in laravel.
In second case its not working,because you work with:
\Illuminate\Support\Collection::class
in first case, you work with :
\Illuminate\Database\Eloquent\Collection::class
To make it work , you can try to do next thing:
take a
\Illuminate\Support\Collection::class
and return it paginated via
Illuminate\Pagination\Paginator::class
so the end result will look like this:
$resting_time = DB::table('alquiler')
->join('socio','alquiler.soc_id','=','socio.id')
->join('pelicula','alquiler.pel_id','=','pelicula.id')
->select('socio.soc_nombre','pelicula.pel_nombre','alquiler.created_at', DB::raw("DATEDIFF(alq_fecha_hasta,NOW()) AS Days"))
->get()->map(function($alquiler){
return ($alquiler->Days >= 0) ? $alquiler : null;
});
$resting_time = $resting_time->filter()->sortBy('Days');
return new Illuminate\Pagination\Paginator($resting_time, 6);
However, i would recommend to prepare data from SQL side, neither doing all of the manipulations from collection perspective.
Most of the answers already provided will work, but will return a collection instead of a paginated resource. The trick is to use the tap helper method before map'ping, to return the same object you modified.
return tap(Alquiler::select(['socio.soc_nombre','pelicula.pel_nombre','alquiler.created_at', DB::raw("DATEDIFF(alq_fecha_hasta,NOW()) AS Days")])
->with('socio', 'pelicula')
->paginate(20))
->map(function ($model) {
return ($model->Days >= 0) ? $model : null;
});
or you can do this way too:
return Alquiler::select(['socio.soc_nombre','pelicula.pel_nombre','alquiler.created_at', DB::raw("DATEDIFF(alq_fecha_hasta,NOW()) AS Days")])
->with('socio', 'pelicula')
->paginate(20))
->map(function ($model) {
if($alquiler->Days >= 0) {
return $model;
}
});
I tried both methods and it didn't work at least the way I wanted, so I did a little more research and put this together:
public function getRestingTime(){
$resting_time = Alquiler::select(['socio.soc_nombre','pelicula.pel_nombre','alquiler.created_at', DB::raw("DATEDIFF(alq_fecha_hasta,NOW()) AS Days")])
->whereRaw('DATEDIFF(alq_fecha_hasta,NOW()) >= ?', [0])
->join('socio','alquiler.soc_id','=','socio.id')
->join('pelicula','alquiler.pel_id','=','pelicula.id')
->orderBy('Days','asc')->paginate(6);
return $resting_time;
}
I hope it helps someone, thanks likewise to the people who responded cleared my mind a bit and gave me new things to try.
This is my code to filter values based on parameters. I want to move it into to a single line. Is there any option available in laravel4?
if($network) //when $network variable has a value.(i will have the same thing for orderby, customer, etc..)
{
return $deals=$mobiles->deals()->where('network','=',$network)->get();// ->orderby($orderby);
}
else
{
return $deals=$mobiles->deals()->get();
}
Go with a ternary:
return $network ? $mobiles->deals()->where('network','=',$network)->get() : $mobiles->deals()->get();
A more clean way than ternary suggested by #moonwave99 (in my opinion) is to add a scope to your model.
public function scopeNetwork($query, $network = null) {
if (null !== $network)
$query->where('network', $network);
return $query;
}
You can then use
return $mobiles->deals()->network($network)->get();
This is an incredibly round-about way of doing this... Id recommend #moonwave99's solution, but you can pass a closure to where(). That, mixed with a boolean, and whereNotNull() can do it. Technically, it's one line of code, but I've broken it apart for the sake of readability.
Easy way:
return $network ? $mobiles->deals->where('network',$network)->get() : $mobiles->deals->get();
Overly complicated way:
return $mobiles->deals->where(isset($network) ? function($query) use ($network){
$query->where('network', $network);
} : function($query){
$query->whereNotNull('id');
})->get();
Im wondering why nobody suggested to use LIKE instead of =. I did like this, and works very well.:) I can have very dynamic filtering with many fields. Make sure the variable in where is set to null when you want to return all element.
My code become
$sorttype=$orderby[0]=='-'?"desc":"asc"; //To get based on assenting or dissenting order
$orderby=$orderby[0]=='-'?substr($orderby, 1):$orderby; //just removing the first element if - existing, because that is just a flag.
if(!$network){$network=null;} //set to null
if(!$orderby){$orderby='id';} //set to id when there no order by
return $deals=$mobiles->deals()
->where('network','LIKE',$network)
->orderby($orderby,$sorttype)
->get();
I'm using a personal request in symfony witch is:
public function findByReferenceUser($Reference, $User)
{
$qb = $this->createQueryBuilder('a')
->select('a')
->join('a.Reference', 'r')
->where('r.id = :refId')
->setParameter("refId", $Reference->getId())
->join('a.User', 'u')
->andWhere('u.id = :userId')
->setParameter("userId", $User->getId());
return $qb->getQuery()->getOneOrNullResult();
}
But it doesn't work properly.
I'm getting 3 results witch are of type NULL,NULL, Boolean. I'm using this to check it:
$list_article = $RArticle->findByReferenceUser($reference, $panier->getUser());
foreach ($list_article as $key => $article)
echo gettype($article);
My database works, and have the right informations.
Finnaly, this works:
$list_article = $RArticle->findAll();
foreach ($list_article as $key => $article)
echo gettype($article);
And print object, object.
So there is my questions: Why do I get NULL, NULL, Boolean in the first case and how do I fixe it?
Thank you for your help! :)
When using getOneOrNullResult, Doctrine will Retrieve a single object. If no object is found null will be returned. here.
If you want several object, you should use getResult
findAll() returns array of objects, such as array('key' => 'object'), that's why you are getting correct records, getOneOrNullResult() returns single object (not array, foreach won't work), if you want to retrieve multiple records you need to use getResult()
I'm using Laravel 4. Say I have an Eloquent model (Patient) and I want to get a patient with the name Bob, I would do this:
$patient = Patient::where('name', '=', 'Bob');
What is the best way to check to see if $patient is a valid record?
If the database query does not find any matching results, it returns null. Therefore...
$patient = Patient::where('name','=','Bob')->first();
if ( is_null($patient) ) {
App::abort(404);
}
(Note: in your original question you forgot ->first() (or ->get()) in your query. Don't forget that or else you will get an Eloquent object instead of a result.)
use this:
$patient = Patient::where('name', '=', 'Bob')->firstOrFail();
it will return Eulqouent model on success or throw ModelNotFoundException upon failure.
I know this is old, but this came up as the 2nd google hit on a search, so . . . for cases where you are not expecting one record or cannot use ->firstOrFail() (my use case is an async typeahead api that returns up to 10 results) the only thing that worked for me was count():
$patient = Patient::where('name', '=', 'Bob')->get(); //you could have more than one bob
if (!count($patient)) {
return 'No records found';
}
$patient = Patient::where('name','Bob')->get();
if ( $patient->isEmpty() ) {
return response(['error' => 'Record not found'], 404);
}
Something like Patient::where('name', '=', 'Bob')->exists() may work. It will return a boolean.
Just use empty() from native php will solve everything, if object null it will return true, if laravel's collection from query builder is empty (but initialized) it will return true too.
$contributor = Contributor::whereVendor('web')->first();
if(empty($contributor)){
...
}
use findOrFail($id) in case of you are passing id parameter to fetch a single record
I ended up on this while seeking solution of ::find($id)->firstOrFail syntax which is error-full.
$patient = Patient::findOrFail($id);
I'm using Doctrine 1.2 and Symfony 1.4.
In my action, I have two different query that return different result set. Somehow the second query seem to change the result (or the reference?) of the first one and I don't have any clue why..
Here is an example:
$this->categories = Doctrine_Query::create()
->from('Categorie AS c')
->innerJoin('c.Activite AS a')
->where('a.archive = ?', false)
->execute();
print_r($this->categories->toArray()); // Return $this->categories results, normal behavior.
$this->evil_query = Doctrine_Query::create()
->from('Categorie AS c')
->innerJoin('c.Activite AS a')
->where('a.archive = ?', true)
->execute();
print_r($this->categories->toArray()); // Should be the same as before, but it return $this->evil_query results instead!
Why Doctrine behave this way ? It's totally driving me crazy. Thanks!
To make it simple it seem like the Query 2 are hijacking the Query 1 result.
Use something like this between queries ($em - entity manager):
$em->clear(); // Detaches all objects from Doctrine!
http://docs.doctrine-project.org/en/2.0.x/reference/batch-processing.html
In the API docs for the toArray() method in Doctrine_Collection it says:
Mimics the result of a $query->execute(array(), Doctrine_Core::HYDRATE_ARRAY);
I suspect to answer this question to your satisfaction you're going to have to go through the source code.
This seems like it has to be an issue of $this->categories and $this->evil_query pointing to the same place. What are the results of $this->evil_query === $this->categories and $this->evil_query == $this->categories?
Hubert, have you tried storing the query into a separate variable and then calling the execute() method on it?
I mean something like:
$good_query = Doctrine_Query::create()
->from('...')
->innerJoin('...')
->where('...', false)
;
$evil_query = Doctrine_Query::create()
->from('...')
->innerJoin('...')
->where('...', true)
;
$this->categories = $good_query->execute();
$this->evil_query = $evil_query->execute();
It seems like both attributes (categories and evil_query) are pointing at the same object.
Erm, after both queries you print_r the result of the first query - change the last line to print_r($this->evil_query->toArray()); to see the difference :)