Multiple ActiveRecord Queries in CodeIgniter - codeigniter

I want to do the following:
//set up insert....
$this->db->insert('property');
$id = $this->db->insert_id();
//some stuff
//set up get
$this->db->where('id', $id);
$id = $this->db->get();
This does not work. It would appear the insert is not being executed before the get - the get is returning zero rows. The insert does (eventually) work. Any suggestions?

You need to give insert some data to insert.
With either the set method:
$this->db->set('name', 'Eric');
$this->db->insert('property');
or by passing an array as the 2nd parameter:
$this->db->insert('property', array(
'name' => 'Eric'
));
As, for your select, you need to tell it what table to select from.
Use either the from method:
$this->db->from('property');
$this->db->where('id', $id);
$id = $this->db->get();
or pass get a table as a parameter:
$this->db->where('id', $id);
$id = $this->db->get('property');
Also, note that get() returns a query object. You need to use ->row (or ->result) to get the data.
$this->db->from('property');
$this->db->where('id', $id);
$query = $this->db->get();
$id = $query->row()->id;

You're missing an argument - insert() takes two:
A table name
An array or object containing columns and values
From the documentation:
$data = array(
'title' => 'My title' ,
'name' => 'My Name' ,
'date' => 'My date'
);
$this->db->insert('mytable', $data);
// Produces: INSERT INTO mytable (title, name, date) VALUES ('My title', 'My name', 'My date')
So, you need to supply insert() with the necessary data by including an array or object as the second argument.
Edit:
Alternatively, you can use the $this->db->set() method for setting values, as explained in Rocket's more comprehensive answer, which also points out you need to specify a table when selecting data.

Related

Laravel what exceptions or errors does update() return when it fails

Until now I used to write my update queries like this:
$flight = App\Flight::where('id', $id)->get();
$flight->name = 'New Flight Name';
$flight->save();
But if I understand it right this will do 2 queries. One select query then one update query.
So I decided to use laravels update() in order to only have 1 query.
eg:
$flight = App\Flight::where('id', $id)
->update(['name' => 'New Flight Name']);
But how can I check if $flight doesnt find anything bases on the id above, or if the $flight update fails?
You could use firstOrFail() function
$flight = App\Flight::where('id', $id)
->firstOrFail()
->update(['name' => 'New Flight Name']);
or do a simple check
$flight = App\Flight::where('id', $id)->first();
if($flight) {
$flight->update(['name' => 'New Flight Name']);
}

Pluck with multiple columns?

When i use pluck with multiple columns i get this:
{"Kreis 1 \/ Altstadt":"City","Kreis 2":"Enge","Kreis 3":"Sihifeld","Kreis 4":"Hard","Kreis 5 \/ Industriequartier":"Escher Wyss","Kreis 6":"Oberstrass","Kreis 7":"Witikon","Kreis 8 \/ Reisbach":"Weinegg","Kreis 9":"Altstetten","Kreis 10":"Wipkingen","Kreis 11":"Seebach","Kreis 12 \/ Schwamendingen":"Hirzenbach"
But i need this?
["Rathaus","Hochschulen","Lindenhof","City","Wollishofen","Leimbach","Enge","Alt-Wiedikon","Friesenberg","Sihifeld","Werd","Langstrasse","Hard","Gewerbechule","Escher Wyss","Unterstrass","Oberstrass","Fluntern","Hottingen","Hirslanden","Witikon","Seefeld","M\u00fchlebach","Weinegg","Albisrieden","Altstetten","H\u00f6ngg","Wipkingen","Affoltern","Oerlikon","Seebach","Saatlen","Schwamendingen-Mitte","Hirzenbach"]
Any suggestion how can i do that? This is my method:
public function autocomplete_districts(Request $request)
{
$district = $request->input('query');
// $ass = /DB::table('districts')->select(array('district', 'region'))->get();
// dd($ass);
$data = Districts::whereRaw('LOWER(district) like ?', [strtolower('%'.$district . '%')])->orWhereRaw('LOWER(region) like ?', [strtolower('%'.$district . '%')])->pluck('region','district');
return response()->json($data);
}
You should use select() with get() and then later on modify the object as you need.
So instead of: ->pluck('region','district');
use: ->select('region','district')->get();
pluck() is advised when you need value of one column only.
And as far as possible, you should have your models singular form not plural (Districts) - to follow Laravel nomenclature.
Cos that is how pluck works. Instead try this.
$data = Districts::whereRaw('LOWER(district) like ?', [strtolower('%'.$district . '%')])->orWhereRaw('LOWER(region) like ?', [strtolower('%'.$district . '%')])->select('region', 'district')->get();
$data = collect($data->toArray())->flatten()->all();
In my case I wanted to pluck 2 values from an array of Eloquent models and this worked:
$models->map->only(['state', 'note'])->values()
That's shorter version of
$models->map(fn($model) => $model->only(['state', 'note']))->values()
This is an issue I constantly have faced and has led me to create the following solution that can be used on models or arrays.
There is also support for dot syntax that will create a multidimensional array as required.
Register this macro within the AppServiceProvider (or any provider of your choice):
use Illuminate\Support\Arr;
/**
* Similar to pluck, with the exception that it can 'pluck' more than one column.
* This method can be used on either Eloquent models or arrays.
* #param string|array $cols Set the columns to be selected.
* #return Collection A new collection consisting of only the specified columns.
*/
Collection::macro('pick', function ($cols = ['*']) {
$cols = is_array($cols) ? $cols : func_get_args();
$obj = clone $this;
// Just return the entire collection if the asterisk is found.
if (in_array('*', $cols)) {
return $this;
}
return $obj->transform(function ($value) use ($cols) {
$ret = [];
foreach ($cols as $col) {
// This will enable us to treat the column as a if it is a
// database query in order to rename our column.
$name = $col;
if (preg_match('/(.*) as (.*)/i', $col, $matches)) {
$col = $matches[1];
$name = $matches[2];
}
// If we use the asterisk then it will assign that as a key,
// but that is almost certainly **not** what the user
// intends to do.
$name = str_replace('.*.', '.', $name);
// We do it this way so that we can utilise the dot notation
// to set and get the data.
Arr::set($ret, $name, data_get($value, $col));
}
return $ret;
});
});
This can then be used in the following way:
$a = collect([
['first' => 1, 'second' => 2, 'third' => 3],
['first' => 1, 'second' => 2, 'third' => 3]
]);
$b = $a->pick('first', 'third'); // returns [['first' => 1, 'third' => 3], ['first' => 1, 'third' => 3]]
Or additionally, on any models you may have:
$users = User::all();
$new = $users->pick('name', 'username', 'email');
// Might return something like:
// [
// ['name' => 'John Doe', 'username' => 'john', 'email' => 'john#email.com'],
// ['name' => 'Jane Doe', 'username' => 'jane', 'email' => 'jane#email.com'],
// ['name' => 'Joe Bloggs', 'username' => 'joe', 'email' => 'joe#email.com'],
// ]
It is also possible to reference any relationship too using the dot notation, as well as using the as [other name] syntax:
$users = User::all();
$new = $users->pick('name as fullname', 'email', 'posts.comments');
// Might return something like:
// [
// ['fullname' => 'John Doe', 'email' => 'john#email.com', 'posts' => [...]],
// ['fullname' => 'Jane Doe', 'email' => 'jane#email.com', 'posts' => [...]],
// ['fullname' => 'Joe Bloggs', 'email' => 'joe#email.com', 'posts' => [...]],
// ]
My solution in LARAVEL 5.6:
Hi, I've just had the same problem, where I needed 2 columns combined in 1 select list.
My DB has 2 columns for Users: first_name and last_name.
I need a select box, with the users full name visible and the id as value.
This is how I fixed it, using the pluck() method:
In the User model I created a full name accessor function:
public function getNameAttribute() {
return ucwords($this->last_name . ' ' . $this->first_name);
}
After that, to fill the select list with the full name & corresponding database id as value, I used this code in my controller that returns the view (without showing users that are archived, but you can change the begin of the query if you like, most important are get() and pluck() functions:
$users = User::whereNull('archived_at')
->orderBy('last_name')
->get(['id','first_name','last_name'])
->pluck('name','id');
return view('your.view', compact('users'));
Now you can use the $users in your select list!
So first, you GET all the values from DB that you will need,
after that you can use any accessor attribute defined for use in your PLUCK method,
as long as all columns needed for the accessor are in the GET ;-)
As far as now Laravel didn't provide such macro to pick specific columns, but anyway Laravel is out of the box and lets us customize almost everything.
Tested in Laravel 8.x
in AppServiceProvider.php
use Illuminate\Support\Collection;
// Put this inside boot() function
Collection::macro('pick', function (... $columns) {
return $this->map(function ($item, $key) use ($columns) {
$data = [];
foreach ($columns as $column) {
$data[$column] = $item[$column] ?? null;
}
return $data;
});
});
Usage
$users = App\Models\User::all();
$users->pick('id','name');
// Returns: [['id' => 1, 'name' => 'user_one'],['id' => 2, 'name' => 'user_two']]
Important notes:
Do not use this macro for a really HUGE collection (You better do it on Eloquent
select or MySQL query select)
Laravel: To pluck multi-columns in the separate arrays use the following code.
$Ads=Ads::where('status',1);
$Ads=$Ads->where('created_at','>',Carbon::now()->subDays(30));
$activeAdsIds=$Ads->pluck('id'); // array of ads ids
$UserId=$Ads->pluck('user_id'); // array of users ids
I have created the model scope
More about scopes:
https://laravel.com/docs/5.8/eloquent#query-scopes
https://medium.com/#janaksan_/using-scope-with-laravel-7c80dd6a2c3d
Code:
/**
* Scope a query to Pluck The Multiple Columns
*
* This is Used to Pluck the multiple Columns in the table based
* on the existing query builder instance
*
* #author Manojkiran.A <manojkiran10031998#gmail.com>
* #version 0.0.2
* #param \Illuminate\Database\Eloquent\Builder $query
* #param string $keyColumn the columns Which is used to set the key of array
* #param array $extraFields the list of columns that need to plucked in the table
* #return \Illuminate\Support\Collection
* #throws Illuminate\Database\QueryException
**/
public function scopePluckMultiple( $query, string $keyColumn, array $extraFields):\Illuminate\Support\Collection
{
//pluck all the id based on the query builder instance class
$keyColumnPluck = $query->pluck( $keyColumn)->toArray();
//anonymous callback method to iterate over the each fileds of table
$callBcakMethod = function ($eachValue) use ($query)
{
$eachQuery[$eachValue] = $query->pluck( $eachValue)->toArray();
return $eachQuery;
};
//now we are collapsing the array single time to get the propered array
$extraFields = \Illuminate\Support\Arr::collapse( array_map($callBcakMethod, $extraFields));
// //iterating Through All Other Fields and Plucking it each Time
// foreach ((array)$extraFields as $eachField) {
// $extraFields[$eachField] = $query->pluck($eachField)->toArray();
// }
//now we are done with plucking the Required Columns
//we need to map all the values to each key
//get all the keys of extra fields and sets as array key or index
$arrayKeys = array_keys($extraFields);
//get all the extra fields array and mapping it to each key
$arrayValues = array_map(
function ($value) use ($arrayKeys) {
return array_combine($arrayKeys, $value);
},
call_user_func_array('array_map', array_merge(
array(function () {
return func_get_args();
}),
$extraFields
))
);
//now we are done with the array now Convert it to Collection
return collect( array_combine( $keyColumnPluck, $arrayValues));
}
So now the testing part
BASIC EXAMPLE
$basicPluck = Model::pluckMultiple('primaryKeyFiles',['fieldOne', 'FieldTwo']);
ADVANCED EXAMPLE
$advancedPlcuk = Model::whereBetween('column',[10,43])
->orWhere('columnName','LIKE', '%whildCard%')
->Where( 'columnName', 'NOT LIKE', '%whildCard%')
->pluckMultiple('primaryKeyFiles',['fieldOne', 'FieldTwo']);
But it returns the \Illuminate\Support\Collection, so if you need to convert to array
$toArrayColl = $advancedPluck->toArray();
if you need to convert to json
$toJsonColl = $advancedPluck->toJson();
To answer the specific question of "how to return multiple columns using (something like) pluck" we have to remember that Pluck is a Collection member function. So if we're sticking to the question being asked we should stick with a Collection based answer (you may find it more beneficial to develop a model-based solution, but that doesn't help solve the question as posed).
The Collection class offers the "map" member function which can solve the posed question:
$data = Districts::whereRaw('LOWER(district) like ?', [strtolower('%'.$district . '%')])->orWhereRaw('LOWER(region) like ?', [strtolower('%'.$district . '%')])
->map(function ($item, $key, $columns=['region','district']) {
$itemArray = [];
foreach($columns as $column){
$itemArray[$column] = $item->$column;
}
return ($itemArray);
});
dd($data);
This should give you a collection where each element is a 2 element array indexed by 'region' and 'district'.
Laravel 8.x, try to use mapWithKeys method instead of pluck, for example:
$collection->mapWithKeys(function ($item, $key) {
return [$key => $item['firstkey'] . ' ' . $item['secondkey']];
});
Expanding on #Robby_Alvian_Jaya_Mulia from above who gave me the idea. I needed it to also work on a relationship. This is just for a single relationship, but it would probably be easy to nest it more.
This needs to be put into AppServiceProvider.php
use Illuminate\Support\Collection;
// Put this inside boot() function
Collection::macro('pick', function (... $columns) {
return $this->map(function ($item, $key) use ($columns) {
$data = [];
foreach ($columns as $column) {
$collection_pieces = explode('.', $column);
if (count($collection_pieces) == 2) {
$data[$collection_pieces[1]] = $item->{$collection_pieces[0]}->{$collection_pieces[1]} ?? null;
} else {
$data[$column] = $item[$column] ?? null;
}
}
return $data;
});
});
Usage:
$users = App\Models\User::has('role')->with('role')->all();
$users->pick('id','role.name');
// Returns: [['id' => 1, 'name' => 'role_name_one'],['id' => 2, 'name' => 'role_name_two']]
Hope this is helpful to someone. Sorry I didn't add this to under #Robby's answer. I didn't have enough reputation.
Pluck returned only the value of the two columns which wasnt ideal for me, what worked for me was this :
$collection->map->only(['key1', 'key2'])->values()

how to delete a single semicolon separated value from database in Codeigniter

My Database table is like below, table name is add_family :
id , Head_id ,Head_Name , CustomerName customer_id
1 , 2589 , Mitesh , anuj^anil , 456^259
2 , 2590 , vijay , amit^ajay , 852^454
if I want to delete any of customer name and its id then how can I delete it in CodeIgniter? e.g if I want to delete only CustomerName Anuj and his customer_id 456 where Head_id is 2586,
how can I delete it?
What you should do is use query builder class and load database library.
Here's an example from official docs:
$data = array(
'CustomerName' => 'anil',//or NULL if you want to 'delete it'
'customer_id' => '259'//or NULL if you want to 'delete it'
);
$this->db->where('Head_id', 2586);
$this->db->update('add_family', $data);
If you want to do it dynamically and you always want the part after the ^ you can use , for example, the php explode function like this:
$this->db->select('CustomerName, customer_id');
$this->db->where('Head_id', 2586);
$query = $this->db->get('add_family');
$result = $query->row_array();
$data = array(
'CustomerName' => explode('^', $result['CustomerName'])[1],
'customer_id' => explode('^', $result['customer_id'])[1]
);
$this->db->where('Head_id', 2586);
$this->db->update('add_family', $data);
Note that you should not update the ID of a table if it's the primary key.
Try something like this. Idea is that to get value from database of both field and replace customer name with empty string
$query = $this->db->get_where('add_family',array('id'=>1));
$name = str_replace("anuj","", $query['CustomerName']);
$id = str_replace(456, "", $query['customer_id ']);
$data = array(
'CustomerName' => $name,//or NULL
'customer_id' => $id//or NULL
);
$this->db->where('Head_id', 2586);
$this->db->update('add_family', $data);

How can i give a where condition before update_batch in codeigniter?

I want to update my table where the input the same input fields names are array and has
add more function which generates the input fields like this:
I want to do update_batch in codeigniter
my model i created a function like this:
This is the code block:
function update_batch_all($tblname,$data=array(),$userid)
{
$this->db->trans_start();
$this->db->where('userid',$userid);
$this->db->update_batch($tblname,$data);
$this->db->trans_complete();
return TRUE;
}
it is not working.
can any one help me that how can i update tables data with update batch that has where condition?
You can read the docs for update_batch() here
Here's the short summary:
You pass in an associative array that has both, your where key, and the update value. As the third parameter to the update_batch() call, you specify which key in your assoc array should be used for the where clause.
For example:
$data = array(
array(
'user_id' => 1,
'name' => 'Foo'
), array(
'user_id' => 2,
'name' => 'Bar'
)
);
$this->db->update_batch($tbl, $data, 'user_id');
Breakdown of arguments passed:$tbl is the table name. $data is the associative array. 'user_id' tells CI that the user_id key in $data is the where clause.
Effect of the above query: Name for user with user_id = 1 gets set to Foo and name for user with user_id=2 gets set to Bar.
In your case, if you want to set the same user_id key in each array with your data array, you can do a quick for loop:
foreach ($data as &$d) {
$d['user_id'] = $user_id;
}
$this->db->update_batch($tbl, $data, 'user_id');
You can use,
$this->db->update_batch($tblname,$data,'user_id');
But all array within data must have a field 'user_id'
eg:
$data=array(
array('user_name'=>'test1','user_id'=>1),
array('user_name'=>'test2','user_id'=>2)
);
You can get more details about update_batch from here

Cakephp 2 Paginate Order By Closest Date

I am trying to get the list of results from the Orders table in my CakePhp 2.x application to sort themselves by the date closest to today.
In a usual mysql query I have had something similar working with say the following syntax:
ABS(DATEDIFF(Order.duedate, NOW()))
However in Cake I am not sure how to get such a custom query to work within the paginate helper. Is this something I may need to set in a finder query in the model?
Here is the controller I currently have (set to a bog standard descending sort)
public function index() {
$this->paginate = array(
'conditions' => array (
'Order.despatched' => array('Not Despatched' , 'Split Despatched')
),
'limit' => 25,
'order' => array('Order.duedate' => 'DESC')
);
$data = $this->paginate('Order');
$this->set('orders', $data);
}
Edit: Using information from the comment below I added a virtual field into the controller which causes an sql error. At first I thought this was due to the associations within the model, to attempt to rectify this I added the virtualField into the Order Model and into the constructor of the associated Account Model. However this made no change to the part of the sql which breaks. The full sql error is:
Error: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'AS `Order__closestdate`, `Account`.`id`, `Account`.`company`, `Account`.`contac' at line 1
SQL Query: SELECT `Order`.`id`, `Order`.`order_id`, `Order`.`orderdate`,
`Order`.`duedate`, `Order`.`rework`, `Order`.`rework_notes`, `Order`.`comments`,
`Order`.`status`, `Order`.`customer`, `Order`.`last_edited`, `Order`.`value`,
`Order`.`quantity`, `Order`.`order_for`, `Order`.`warehouse`, `Order`.`haulier`,
`Order`.`terms`, `Order`.`type`, `Order`.`despatched`, `Order`.`despatched_date`,
`Order`.`invoiced`, `Order`.`invoice_date`, `Order`.`bookingref`,
`Order`.`purchaseref`, `Order`.`payment_due`, `Order`.`payment_status`,
`Order`.`payment_date`, (ABS(DATEDIFF(duedate, NOW())) AS `Order__closestdate`,
`Account`.`id`, `Account`.`company`, `Account`.`contact`, `Account`.`phone`,
`Account`.`email`, `Account`.`postcode`, `Account`.`created`, `Account`.`modified`,
`Account`.`customer`, `Account`.`address1`, `Account`.`address2`, `Account`.`town`,
`Account`.`county`, (ABS(DATEDIFF(duedate, NOW())) AS `Account__closestdate` FROM
`hunter_om`.`orders` AS `Order` LEFT JOIN `hunter_om`.`accounts` AS `Account` ON
(`Order`.`customer` = `Account`.`customer`) WHERE `Order`.`despatched` IN ('Not
Despatched', 'Split Despatched') ORDER BY (ABS(DATEDIFF(duedate, NOW())) DESC LIMIT 25
For reference the code in the models are:
//Order Model
public $virtualFields = array(
'closestdate' => 'ABS(DATEDIFF(duedate, NOW())'
);
//Account Model
public function __construct($id = false, $table = null, $ds = null) {
parent::__construct($id, $table, $ds);
$this->virtualFields['closestdate'] = $this->Order->virtualFields['closestdate'];
}
Any help is appreciated thanks.
You'll need to add a 'virtualField' to the Order model before paginating;
$this->Order->virtualFields['closestdate'] = "ABS(DATEDIFF(Order.duedate, NOW()))";
Then use that field as a regular field in your query/paginate
public function index() {
$this->Order->virtualFields['closestdate'] = "ABS(DATEDIFF(Order.duedate, NOW()))";
$this->paginate = array(
'conditions' => array (
'Order.despatched' => array('Not Despatched' , 'Split Despatched')
),
'limit' => 25,
'order' => array('Order.closestdate' => 'DESC')
);
$data = $this->paginate('Order');
$this->set('orders', $data);
}

Resources