Automatic object append - php

I created a class which generates queries based on arguments I feed it via PHP object chaining. Select, insert, update and delete all define $this->query so nothing is left over from previous calls.
Here is an example, keeping in mind that where() might be succeeded by order(), limit(), etc.
$tQuery->select('id', 'users')
->where('email', '=', $email)
->end();
All end() does is append a ; to the SQL statement and returns it as a string, so it seems a bit intrusive using it in every single call.
public function end()
{
$this->query .= ';';
return $this->query;
}
Is there a way (magic method or otherwise) I can automatically do this each time I call the class so I don't require this method every time?
$tQuery->select('id', 'users')
->where('email', '=', $email);
i.e. "You're not asking me for any more methods, I'll just return what I have with a ; at the end"

I guess that the result of end() is used by some pdo to execute the final query. This means, you need a string-presentation of your query-object in the end. Here, php's type-casting may come in handy. There is a magic method available for objects when using them as string, __toString()
Consider this example:
<?php
echo $tQuery->select('id', 'users')
->where('email', '=', $email)
->end();
Now, add a __toString() method to your class:
public function __toString()
{
return $this->end();
}
And you may rewrite the above to
<?php
echo $tQuery->select('id', 'users')
->where('email', '=', $email);
This works for any object that is used in a string-context, e.g. this also triggers __toString():
<?php
$sqlString = (string)$tQuery->select('id', 'users')
->where('email', '=', $email);

Related

Use contain() of ArrayCollection() class in DQL

I have this script.
It fetch the rows from DB and choose the row which belongs to a Category(CatData).
getCatDatas() returns \Doctrine\Common\Collections\ArrayCollection() class.
So it can use contain() to check.
However I want to put this method in DQL itself, is there any practice??
$result = array();
$articles = $em->createQuery("SELECT a FROM DefaultBundle:ArticleData a ")
->getResult();
foreach ($articles as $a){// I want to put this function in DQL.
if ($a->getCatDatas()->contain($cat)){
array_push($articles,$result);
}
}
Yes, you can use DQL and do a where condition on relation.
You can find more info here: https://symfonycasts.com/screencast/symfony3-doctrine-relations/relation-query
In particular, the method findAllRecentNotesForGenus in GenusNoteRepository.
I think you can do something similar:
public function findPostsByCategoryData(CategoryData $cat)
{
return $this->createQueryBuilder('a')
->andWhere('a.catDatas = :cat')
->setParameter('cat', $cat)
->getQuery()
->execute();
}
#Alessandro Filira started in the right direction but forgot to take into account that this is a to-many relationship that you want to filter on.
Doctrine supports MEMBER OF operator, which works a bit like IN but in the opposite direction. It allows you to check if the specific value that you have is found as an element in the related group.
So the code should be like:
public function findPostsByCategoryData(CategoryData $cat): array
{
return $this->createQueryBuilder('a')
->andWhere(':cat MEMBER OF a.catDatas')
->setParameter('cat', $cat)
->getQuery()
->getResult();
}
See Doctrine Docs for MEMBER OF usage: https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/dql-doctrine-query-language.html (use Ctrl+F and "member of")

Error in Laravel 5.2 Eloquent - cannot be converted to String

I am very new to Laravel and am going through the tutorials and am stuck on something.
I have a complex query that I need to reuse with one parameter change in the where clause. I added this as a query scope in my Model and then call it from my corresponding Controller. When I try to return the data though I am getting this error:
Object of class Illuminate\Database\Eloquent\Builder could not be converted to string
Here is the query scope:
public function scopeCrosstab($wellID)
{
return static::select('sampleDate', \DB::raw("
max(if(chemID=1, pfcLevel, ' ')) as 'PFOA', max(if(chemID=1, noteAbr, ' ')) as 'PFOANote'
"))
->leftJoin('SampleNote', 'WellSample.noteID', '=', 'SampleNote.noteID')
->where('wellID', '=', $wellID)
->groupBy('sampleDate');
}
Here is the Controller code:
public function smith()
{
$wellSamples = WellSample::crosstab(2);
return $wellSamples->get();
//return view('pages.wellsample', compact('wellSamples'));
}
I have tried many different permutations of the code with quotes, with double quotes etc. If I hard code the value in the query scope it works, but I need to be able to make it dynamic.
Scope methods take at least one parameter, the first of which must be $query. You then build off of the query variable that is passed to your scope method. Like this:
public function scopeCrosstab($query, $wellID)
{
return $query->select('sampleDate', \DB::raw("
max(if(chemID=1, pfcLevel, ' ')) as 'PFOA', max(if(chemID=1, noteAbr, ' ')) as 'PFOANote'
"))
->leftJoin('SampleNote', 'WellSample.noteID', '=', 'SampleNote.noteID')
->where('wellID', '=', $wellID)
->groupBy('sampleDate');
}

Laravel query builder returns object or array?

I'm building a very simple web app with Laravel.
I've built two separate Controllers, which each return two separate views, as follows:
ProfileController:
class ProfileController extends BaseController {
public function user($name)
{
$user = User::where('name', '=', $name);
if ($user->count())
{
$user = $user->first();
$workout = DB::table('workouts')->where('user_id', '=', $user->id)->get();
Return View::make('profile')
->with('user', $user)
->with('workout', $workout);
}
return App::abort(404);
}
}
WorkoutController:
class WorkoutController extends BaseController {
public function workout($name)
{
$workout = DB::table('workouts')->where('name', '=', $name)->first();
if ($workout)
{
Return View::make('add-exercise')
->with('workout', $workout);
}
return App::abort(404);
}
}
What is confusing me is what I had to do in order to pass a single workout object to each view. As you might have noticed the query builders for workout are different:
$workout = DB::table('workouts')->where('user_id', '=', $user->id)->get();
and
$workout = DB::table('workouts')->where('name', '=', $name)->first();
On the profile view, I get an object using the ->get(); method, but on the add-exercise view, I must use ->first(); or I will otherwise get an array with only one index, where I can then access the object, i.e. $workout[0]->name instead of $workout->name.
Why is this? Shouldn't I be able to use either get and/or first in both controllers and expect the same type of result from both since I want the same thing from the same table?
get() returns a collection of objects every time. That collection may have 0 or more objects in it, depending on the results of the query.
first() calls get() under the hood, but instead of returning the collection of results, it returns the first entry in the collection (if there is one).
Which method you use depends on what you need. Do you need the collection of all the results (use get()), or do you just want the first result in the collection (use first())?
Model::find(numeric); returns a object
Model::whereId(numeric)->first(); returns a object
Model::whereId(numeric)->get(); - returns a collection
Model::whereId(numeric); - returns a builder

Undefined Variable Multiple Query Scopes Laravel

This work perfect:
public function scopeHBO($query)
{
return $query ->where('network', '=', "hbo");
}
Call in Controller: It Works!
$events = Schedule::HBO()->orderBy('searchdate')->get();
When I add another Query Scope like so:
public function scopeHBO($query)
{
return $query
->where('network', '=', "hbo")
->where('searchdate', '>=', 'NOW()');
}
OR:
public function scopeDate($query)
{
return $query->where('searchdate', '>= ', 'NOW()');
}
Then call in the controller:
$events = Schedule::HBO()->Date()->orderBy('searchdate')->get();
I get an error: Undefined variable: event. I tried with with Raw MySql in the same model and it works. Whenever i add a query scope, does not matter what it is.. i get that same error Undefined variable: event.
NOW() is a function, so you need to use a raw query:
where('searchdate', '>=', DB::raw('NOW()'))
Then you can use the scopes. (Do note that I think scopeDate must be called as date(), not Date() - not 100 % sure on that though.)
This sounds less like a generic problem with Laravel, and more like a problem with you specific application.
My guess (which is a wild guess), is that adding that second where clause in your scope method
return $query
->where('network', '=', "hbo")
->where('searchdate', '>=', 'NOW()');
ended up creating a SQL query that returned 0 rows. Then, somewhere in your other code you're doing something like
foreach($events as $event)
{
//...
}
//referencing final $event outside of loop
if($event) { ... }
As I said, this is a wild guess, but the problem doesn't seem to be your query code, the problem seems to be the rest of your code that relies on the query returning a certain number of, or certain specific, rows/objects.

Method Chaining based on condition

How can you do method chaining based on condition in laravel 4 ? Say if one value is not false then the method inside will be chained to the method called before the if statement.
Is it possible in laravel?
$data = User::where('username', $somevariable );
if(isset( $somevar_again ))
{
$data->where('age', 21);
}
$data->orderBy('reg_date', 'DESC')->get();
return $data->first();
// tried code above and its giving me wrong result
in codeigniter I can do this
$this->db->select('e.*, v.name_en as v_name_en')
->from($this->table_name . ' e, ' . $this->ptc_venues . ' v');
$this->db->where('e.venue_id_en = v.id');
if(isset($search)){
$this->db->where('(v.name_en LIKE "%'.$search.'%")');
}
$this->db->limit($limit, $start);
$this->db->order_by('e.added_date_en', 'DESC');
I believe your problem happened because you didn't store back the resulting query after each query builder method call.
$query = User::query();
// Checking for username if exists
if (!empty($username)) {
$query = $query->where('username', $username);
}
// Check for age if exists
if (isset($age)) {
$query = $query->where('age', $age);
}
// Ordering
$query = $query->orderBy('reg_date', 'DESC');
// Get the first result
// After this call, it is now an Eloquent model
$user = $query->first();
var_dump($user);
From Laravel 5.2 and onward, you can utilise Conditional Clauses/Statements:
Sometimes you may want statements to apply to a query only when
something else is true. For instance you may only want to apply a
where statement if a given input value is present on the incoming
request. You may accomplish this using the when method
The when method only executes the given Closure when the first parameter is true. If the first parameter is false, the Closure will not be executed.
You can use the code as follows:
$data = User::where('username', $somevariable)
->when( isset($somevar_again), function ($query) {
return $query->where('age', 21);
})
->orderBy('reg_date', 'DESC')
->get();
return $data->first();
Also, note that Laravel 5.3+, it has further been extended as documented below:
You may pass another Closure as the third parameter to the when
method. This Closure will execute if the first parameter evaluates as
false

Categories