preserving aliases in laravel query builder - php

function getPerson(){
return \DB::table('user')->select('firstname as name', 'age');
}
function getPet(){
return \DB::table('pet')->select('petname as name', 'age');
}
function getNames($var){
return $var->select('name')->where('age', 10)->get();
}
$base = getNames(getPerson());//empty here
$base = getNames(getPet());//empty here
The main problem is I got a lot of different queries and I want to put aliases on them and then prepare data for diagram with another query. It is difficult if aliases diseappear.

Each query can have only one select(). $var being passed into getNames() is a query already has select() so adding more select() will override previous one.
Try remove select() from getNames() and test whether you get your result.
function getNames($var){
return $var->where('age', 10)->get();
}
Remember that you are using 'query builder' which means every query constraint you are adding is being added into the same query, not different one. The entire query will be actually built and executed once you try to get the result data set like calling get(), first(), etc.

Related

Route binding with composite key

I am trying to bind a model that has composite key. Take a look, at first place I define my route:
Route::get('laptop/{company}/{model}', 'TestController#test');
Now, I define as I want to be resolved:
$router->bind('laptop', function ($company, $model) {
$laptop = ... select laptop where company=$company and ...;
return $laptop;
});
Now, I see how I am injecting the class in order to get the laptop in the controller: function into to test the resolution:
function test(Laptop $laptop){
return 'ok';
}
However, I am receiving the following error:
BindingResolutionException in Container.php line 839:
I assume that the error is caused by $router->bind('laptop' because it should matches a unique placeholder in the url ("company" or "model"). In my case I get lost because I need to matches both at the same time.
Note: I am not using db/eloquent layer. This problem is focused in the way on how to resolve route binding with multiples keys representing an unique object.
I am not sure if is it possible or if am I missing something. Thank you in advance for any suggestion.
Laravel does not support composite key in eloquent query.
You need to use query builder method of laravel to match against both values. ie: DB::select()->where()->where()->get();
Just put select and where conditions in above.
If you bind $router->bind('laptop', ...); then your route parameter should be Route::get('{laptop}', ...);. There is two possibility to query a laptop by model and company as you expected.
The safest way is query laptop on your controller:
Route::get('laptop/{company}/{model}', 'TestController#test');
In you TestController.php
function test(Laptop $laptop, $company, $model){
return $laptop->whereCompany($company)->whereModel($model)->first();
}
Another solution is allow slashes on your route parameter:
Route::get('laptop/{laptop}', 'TestController#test')->where('laptop', , '(.*)?');
and your binding function could be:
$router->bind('laptop', function ($laptop) {
$laptop = explode('/', $laptop);
$company = current($laptop);
$model = end($laptop);
if ((count($laptop) === 2) && ($result = App\Laptop::whereCompany($company)->whereModel($model)->first()) {
return $result;
}
return abort(404);
}

Set Query Builder conditions to Model in Laravel 5.1

First I have to say that I tried to find solution, and i didn't.
Basic question:
$Br = new BrandTop;
dd( $Br->limit(10)->get() ); // Will return 10 rows
and
$Br = new BrandTop;
$Br->limit(10);
dd( $Br->get() ); // Will return all rows.
So, the basic question - why? How can I set some limit for Model, but still work with it, for example set (or not set) some where or order depends on other variables.
Advanced question:
I want to use Model like this:
class BrandTop extends Model
{
public function withBrand() {
return $this->leftJoin('brand', 'brand.id' , '=', 'brandtop.brand_id');
}
public function forType($type) // there is much more conditions for type
{
return $this->where(['type' => $type]);
}
// main function
public function forSunglasses($limit = 0, $logo = false)
{
if ($logo)
$this->where(['menu_logo' => 1])->orderBy('total_sales', 'desc');
if ($limit)
$this->limit($limit);
return $this->forType('sunglasses')->withBrand();
// But there goes Error, because forType() return Builder object, and it has no withBrand() method
}
}
So, there is much more conditions, and it's much easier to set all conditions in separate methods. But how?
Model vs Builder
The thing to understand here is the difference between the Model object and the underlying Builder (query builder) object.
The statement $Br = new BrandTop; will create a new instance of a Model, and assign it to the $Br variable. Next, the $Br->limit(10) statement will create a new instance of a Builder object for the brand_tops table, with a limit of 10 applied.
In your first example, by doing $Br->limit(10)->get(), you're calling get() on the Builder that has your limit applied.
In your second example, your individual $Br->limit(10) creates the new Builder instance, but never uses it for anything. The next statement, $Br->get(), creates another new Builder instance without any constraints, so it retrieves all the records.
To be able to build up your query, you need to assign your Builder instance to a variable, and continue to modify that instance before finally calling get(). For example, to get your second example to work:
$query = BrandTop::query();
$query->limit(10);
$query->where(/*conditions*/);
dd($query->get());
Query Scopes
In relation to the second part of your question, you probably want to look into query scopes.
class BrandTop extends Model
{
// renamed to "JoinBrand" instead of "WithBrand", as "with" would imply
// an eager loaded relationship vs a joined table
public function scopeJoinBrand($query)
{
return $query->leftJoin('brand', 'brand.id' , '=', 'brandtop.brand_id');
}
// got rid of "for" prefix
public function scopeType($query, $type)
{
return $query->where('type', $type);
}
// got rid of "for" prefix
public function scopeSunglasses($query, $limit = 0, $logo = false)
{
if ($logo)
$query->where(['menu_logo' => 1])->orderBy('total_sales', 'desc');
if ($limit)
$query->limit($limit);
return $query->type('sunglasses')->joinBrand();
}
}
With the above model, your code would look something like:
dd(BrandTop::sunglasses()->get());
// or, more verbosely:
$query = BrandTop::query();
$query->sunglasses(); // $query already an object, no need to reassign it to itself
dd($query->get());

Multiple queries in laravel scope function

I am attempting to make multiple queries in a scope function in laravel. my code is as follows. the first query executes normally but the second seems to be ignored. what is the correct way to do this?
public function scopeUpdateStatus($query,$oldUser, $newUser, $alias, $env) {
$query->where('db_conn_app_alias_user', $newUser)->where('db_conn_app_alias', $alias)->where('app_instance_environment', $env)->update(array('user_status' => 'active'));
$query->where('db_conn_app_alias_user', $oldUser)->where('db_conn_app_alias', $alias)->where('app_instance_environment', $env)->update(array('user_status' => 'passive'));
return "success";
}
The trick here is to use with (a Laravel helper function) and clone.
function scopeSomeName($query) {
with(clone $query)->whereStuff;
with(clone $query)->whereOtherStuff;
}
This happens because you use the same $query variable in the two updates. You add where()s to the $query in the first update query and then run it, but when you add your where()s in the second update query the where()s from the first query are still there. Because of this your query will return zero result so there is nothing to update. Copy the $query first to a new variable then run the second query in the copied one:
public function scopeUpdateStatus($query, $oldUser, $newUser, $alias, $env) {
$queryTemp = $query;
$query->where('db_conn_app_alias_user', $newUser)
->where('db_conn_app_alias', $alias)
->where('app_instance_environment', $env)
->update(array('user_status' => 'active'));
$queryTemp->where('db_conn_app_alias_user', $oldUser)
->where('db_conn_app_alias', $alias)
->where('app_instance_environment', $env)
->update(array('user_status' => 'passive'));
return "success";
}
A minor edit (reduces overhead) to #merlinpatt answer. You don't need to clone the $query twice. Just once, as you already have the existing/original variable
function scopeSomeName($query) {
$query_cloned = clone $query;
$query->whereStuff;
$query_cloned->whereOtherStuff;
}
Also, there's no need for the with() helper function.
Tested it and works as expected.

Decrement function, decrement all the rows in the tables

Im using laravel v3.2.12-4, and I have an problem with the decrement function. Instead of update only one row, this method affects all the rows in the column. Im using Eloquent, and I have a many_to_many relationship.
The code that constains the decrement method is:
foreach ($ids as $id) {
$indicator = Indicator::find($id);
$tags = $indicator->tags()->get();
foreach ($tags as $tag) {
$indicator->tags()->detach($tag->id);
if ($tag->frequency == 1) {
$tag->delete();
} else {
// I have to made this code to fix the problem with decrement function
// But i want to use decrement
$tag->frequency = $tag->frequency - 1;
$tag->save();
// This dosnt work for me.
// $tag->decrement('frequency');
}
}
$indicator->delete();
}
In the model class Indicator i made the relation with this function:
public function tags()
{
return $this->has_many_and_belongs_to('Tag');
}
In the model class Tag i made the relation with this function:
public function indicators()
{
return $this->has_many_and_belongs_to('Indicator');
}
Well, if I made an update to the column this result OK for me, but when If I use the decrement function this affect all the rows and I don't know if this a bug or something with this method.
Thanks.
This is how it's designed. The decrement() method is actually defined on the Query Builder and not on Eloquents builder. What this means is that when you call $tag->decrement('frequency') it's actually falling through to the QB and simply running a query like UPDATE tag SET frequency=frequency - 1. Notice that there's no WHERE clause?
You could still use the decrement() method but you'd have to do it like this.
$tag->where_id($tag->id)->decrement('frequency');
Now you've set the WHERE clause and only that tag will be decremented. Of course, the cleaner solution is what you've got. Or perhaps this.
$tag->frequency--;
Not tested, may throw an error of some sort.

How to find query executed successfully or not?

im using codeigniter 2.0.2 and this is from its userguide
$data = array(
'title' => $title,
'name' => $name,
'date' => $date
);
$this->db->where('id', $id);
$this->db->update('mytable', $data);
my question is once this executed how do you find its executed correctly or not?
The update function returns a value:
$result = $this->db->update('mytable', $data);
Check that value for either being TRUE (success) or FALSE (failure). update runs query internally and then returns the return value of query (Ref):
The query() function returns a database result object when "read" type queries are run, which you can use to show your results. When "write" type queries are run it simply returns TRUE or FALSE depending on success or failure.
Use
$this->db->affected_rows()
to see how many rows have been affected on write type queries (update, insert, etc...)
http://codeigniter.com/user_guide/database/helpers.html
Both answers was valid. You just have to use each one depending on the case. If you are just checking if the query was executed use this method:
$result = $this->db->update('mytable', $data);
if you really want the number of rows affected the second method is better:
$this->db->affected_rows()
however I always use the second method. The update query is a good example why. A query can be successful and still there was nothing updated on the database because the value that you were trying to update was actually equal to the value you are sending in the query.
This would be a false positive. And the affected rows would be 0.
I hope it helped =)
When developing CodeIgniter model methods, I find that I consistently return desirable values depending on the type of database write that is executed. It is often important to differentiate between a query that has run successfully versus a query that has actually changed a record.
For an update or delete query, I'll return the number of affected rows -- this will be most helpful to controller methods that call it. If you are performing logging (to keep track of change history), then ONLY log something if there is a change to the row; otherwise you are unnecessarily bloating your change history logs.
public function update(int $id, array $newData) :int
{
$oldData = $this->db->get_where('mytable', ['id' => $id])->row_array();
if ($this->db->update('mytable', $newData, ['id' => $id])) {
$affectedRows = $this->db->affected_rows();
if ($affectedRows) {
$this->Log->mytableUpdate($id, $newData, $oldData);
}
return $affectedRows;
}
return 0;
}
For insert queries, I always return the auto-incremented id of the newly inserted row via insert_id().
If using the PDO driver with PostgreSQL, or using the Interbase driver, this function requires a $name parameter, which specifies the appropriate sequence to check for the insert id.
public function insert(array $newData) :int
{
if ($this->db->insert('mytable', $newData)) {
$newId = $this->db->insert_id(); // or insert_id('mytable')
$this->Log->mytableInsert($newId, $newData);
return $newId;
}
return 0;
}
Having consistent return types in your model methods will make your project easier to develop and maintain. The script that calls these model methods will be able to quickly assess the outcome by making a "falsey" check.

Categories