Codeigniter 4 Query Builder loses SELECT and WHERE condition while executing once more - codeigniter-4

I am using Codeigniter 4 Query Builder.
Following is code to retrieve data from the database
public function get_data()
{
$where = [
'username' => 'admin'
];
$this->_builder = $this->_db->table('pf_user_master s');
$this->_builder->join('pf_role_master r', 'r.role_id = s.role_id');
$this->_builder->select('username, first_name, last_name, mobile, email, r.role_name, s.status');
if (is_array($where) && !empty($where)) {
$this->_builder->where($where);
}
$first= $this->_builder->get()->getResultArray();
print_r($first);
$second= $this->_builder->get()->getResultArray();
print_r($second);
exit;
}
I am getting the following output:
In variable $first I am getting output as expected
Array
(
[0] => Array
(
[username] => admin
[first_name] => Fisrt
[last_name] => Last
[mobile] =>
[email] => first.last#gmail.com
[role_name] => Admin
[status] => 1
)
)
But in variable $second it Query Builder loses SELECT, WHERE condition and also JOIN.
And prints the output as follows:
Array
(
[0] => Array
(
[user_id] => 1
[username] => admin
[password] => 21232f297a57a5a743894a0e4a801fc3
[first_name] => First
[last_name] => Last
[mobile] =>
[email] => first.last#gmail.com
[role_id] => 1
[status] => 1
[created_by] =>
[created_date] => 2020-09-08 19:30:52
[updated_by] =>
[updated_date] => 2020-09-08 19:32:42
)
[1] => Array
(
[user_id] => 2
[username] => superadmin
[password] => 21232f297a57a5a743894a0e4a801fc3
[first_name] => NewFirst
[last_name] => NewLast
[mobile] =>
[email] => new.new#gmail.com
[role_id] => 1
[status] => 1
[created_by] =>
[created_date] => 2020-09-08 21:51:42
[updated_by] =>
[updated_date] =>
)
)

I've not tested this and it comes from reading the documentation only.
In the CodeIgniter documentation get() has the following parameters
get([$limit = NULL[, $offset = NULL[, $reset = TRUE]]]])
Reference: https://codeigniter.com/user_guide/database/query_builder.html#get
If you were to use
$first= $this->_builder->get(NULL,NULL,FALSE)->getResultArray();
Then your code would become:
public function get_data()
{
$where = [
'username' => 'admin'
];
$this->_builder = $this->_db->table('pf_user_master s');
$this->_builder->join('pf_role_master r', 'r.role_id = s.role_id');
$this->_builder->select('username, first_name, last_name, mobile, email, r.role_name, s.status');
if (is_array($where) && !empty($where)) {
$this->_builder->where($where);
}
$first= $this->_builder->get(NULL,NULL,FALSE)->getResultArray();
print_r($first);
$second= $this->_builder->get()->getResultArray();
print_r($second);
exit;
}
Of course if you were to perform this a 3rd time, the 2nd instance should cause a reset.
The Code is the Documentation ( in most cases ) so we have
In CodeIgniter 4.04 - /system/Database/BaseBuilder.php - Line 1824
The code is
/**
* Get
*
* Compiles the select statement based on the other functions called
* and runs the query
*
* #param integer $limit The limit clause
* #param integer $offset The offset clause
* #param boolean $reset Are we want to clear query builder values?
*
* #return ResultInterface
*/
public function get(int $limit = null, int $offset = 0, bool $reset = true)
{
if (! is_null($limit))
{
$this->limit($limit, $offset);
}
$result = $this->testMode
? $this->getCompiledSelect($reset)
: $this->db->query($this->compileSelect(), $this->binds, false);
if ($reset === true)
{
$this->resetSelect();
// Clear our binds so we don't eat up memory
$this->binds = [];
}
return $result;
}
So the default for $reset, will "clear" what you have observed.

As mentioned by TimBrownlaw
I changed
$first= $this->_builder->get()->getResultArray();
to
$first= $this->_builder->get(NULL, 0 , false)->getResultArray();
and it worked for me as it didn't reset the query as third parameter of get() is for resetting the query

Related

How to export data of array to excel with each element from an array storing in single row

I am trying to export data to excel using Fast excel. This is easy for straight forward export. However, I have data as follows:
Illuminate\Support\Collection Object
(
[items:protected] => Array
(
[0] => stdClass Object
(
[id] => 1
[name] => name1
[multiple_units] => ["80","103","126","7","10","13"]
)
[1] => stdClass Object
(
[id] => 2
[name] => name2
[multiple_units] => ["30","23","26","7","25","33"]
)
)
)
Where multiple_units is a text column with json_decode. So, now when I try to export data with following code:
public function exportTest()
{
$reviews = DB::table('test_db')->get();
$file_name = 'Review - '.date('Y_m_d').'.xlsx';
return (new FastExcel($reviews))->download($file_name,function($review){
$unit_lists = '';
if($review->multiple_units != NULL){
$unit_ids = json_decode($review->multiple_units, true);
foreach($unit_ids as $uk => $uv){
return [
'Name' => $review->name,
'Units' => $uv
];
}
}
});
}
It export to excel file like as:
Name Units
name1 80
name2 30
However, I want to export with each unit being in a single row. For instance,
Name Units
name1 80
name1 103
name1 126
name1 7
name1 10
name1 13
...
...
...
...
As far as I can see, Fast Excel does not allow changing the number of rows in the callback function.
The solution is to manipulate the data before passing it to Fast Excel:
public function exportTest()
{
$reviews = DB::table('test_db')->get()->flatMap(function ($review) {
$items = [];
if ($review->multiple_units != NULL) {
$unit_ids = json_decode($review->multiple_units, true);
foreach ($unit_ids as $uk => $uv) {
$items[] = [
'Name' => $review->name,
'Units' => $uv
];
}
}
return $items;
});
$file_name = 'Review - '.date('Y_m_d').'.xlsx';
return (new FastExcel($reviews))->download($file_name);
}
This will map over each review return an array containing name and units for each unit. Then the array is flattened and passed to Fast Excel.
Note: This will ignore any reviews where review->multiple_units == NULL (which includes an empty string)
public function exportTest() {
$reviews = DB::table('test_db')->orderBy('name')->get();
$file_name = 'Review - '.date('Y_m_d').'.xlsx';
return (new FastExcel($reviews))->download($file_name,function($reviews) {
foreach ($reviews as $review) {
# code...
if(!empty($review->multiple_units)) {
$unit_ids = json_decode($review->multiple_units, true);
foreach($unit_ids as $uk => $uv){
return [
'Name' => $review->name,
'Units' => $uv
];
}
}
}
});
}

How to call Recursive function for array data Using Promises in Node Js

I am trying to call the recursive function in my code. The loop executing first time, but recursive function which is called inside the function is not working.
Here is my code :
function buildDynamicMenu(elements,parentId)
{
branch =new Array();
elements.forEach(function(element){
if (element['parent_id'] == parentId) {
children = buildDynamicMenu(elements, element['menu_id']); //Recursive function not working
if (children) {
element['children'] =children;
}
branch = element;
}
});
return branch;
}
var parentId=0;
buildDynamicMenu(data); // Array data
I have added sample array which I am using for this logic, array has menu and Submenu Id, we have to call recursive function to loop through all sub array
Array
(
[0] => Array
(
[menu_id] => 1
[menu_name] => Home
[parent_id] => 0
[link] => #home
)
[1] => Array
(
[menu_id] => 2
[menu_name] => Web development
[parent_id] => 0
[link] => #web-dev
)
[2] => Array
(
[menu_id] => 3
[menu_name] => WordPress Development
[parent_id] => 2
[link] => #wp-dev
)
[3] => Array
(
[menu_id] => 4
[menu_name] => About w3school.info
[parent_id] => 2
[link] => #w3school-info
)
[4] => Array
(
[menu_id] => 7
[menu_name] => Javascript
[parent_id] => 2
[link] => #
)
[5] => Array
(
[menu_id] => 8
[menu_name] => Plugins
[parent_id] => 7
[link] => #plugin-dev
)
)
seems like this doesn't have a pointed buildDynamicMenu export. check if there is one in 'this'. try to assign the buildDynamicMenu function to some const and perform call to it.
Ok here is my demo func working very well in nodejs typescript:
function recursieve (p) {
console.log('in recursieve ', p);
if (p > 0) {
recursieve (--p);
} } recursieve (10);// rec call
So in my opinion there are two problems in your code:
1. 'this' was not pointed to the function.
2. recursieve condition, like the elements is empty, or id is no match.

Wordpress Pagination with Custom Query

I am attempting to write a plugin that searches an SQL table based on input supplied via a form. My code is...
global $table_name_log;
$pagenum = isset( $_GET['pagenum'] ) ? absint( $_GET['pagenum'] ) : 1;
$limit = 25;
$offset = ( $pagenum - 1 ) * $limit;
$l_task=isset( $_POST['l_task'] ) ? $_POST['l_task'] : "a";
$l_val=$_POST['l_val']; if($l_val=="") { $l_task="a"; }
if($l_task=="d")
{
$criteria="where log.Doc_Number='{$l_val}'";
}
elseif($l_task=="e")
{
$criteria="where log.Employee='{$l_val}'";
}
else
{
$criteria="";
}
$SQLQuery="SELECT em.ID, log.Date, em.Payroll, log.Doc_Number, doc.Doc_Name, doc.Replaced_By, log.Trainer, log.Log, log.Type
FROM {$table_name_log} log
join {$table_name_employee} em on log.Employee=em.ID
join {$table_name_documents} doc on log.Doc_Number=doc.Doc_Number
{$criteria}
order by log.Employee asc
";
//Get total number of results
$results=$wpdb->get_results("{$SQLQuery}",ARRAY_A);
$search_total = $wpdb->num_rows;
//Limit results by page
$results=$wpdb->get_results("{$SQLQuery} LIMIT {$offset}, {$limit}",ARRAY_A);
if(! $results)
{
if($pagenum>1)
{
$pagenum=1;
$results=$wpdb->get_results("{$SQLQuery} LIMIT 0, {$limit}",ARRAY_A);
}
}
I then display the results of the query.
At the bottom of the displayed results I have this code...
$total=$search_total;
$num_of_pages = ceil( $total / $limit );
$page_links = paginate_links( array(
'base' => add_query_arg( 'pagenum', '%#%' ),
'format' => '',
'prev_text' => __( '«', 'aag' ),
'next_text' => __( '»', 'aag' ),
'total' => $num_of_pages,
'current' => $pagenum
) );
if ( $page_links ) {
echo '<div class="tablenav"><div class="tablenav-pages" style="margin: 1em 0">' . $page_links . '</div></div>';
}
The problem is that although the number of pages is calculated correctly and page 1 displays the correct results; when selecting another page it displays the results without the $criteria set by $l_task. How do I edit the code so that the pagination links feed the correct query to use for the additional pages?
Works now by adding this at the top...
if(isset($_GET['l_task'])) { $_POST['l_task']=$_GET['l_task']; }
if(isset($_GET['l_val'])) { $_POST['l_val']=$_GET['l_val']; }
I then added this...
'add_args' => array( 'l_task' => $_POST['l_task'], 'l_val' => $_POST['l_val'] )
to...
$page_links = paginate_links( array(
'base' => add_query_arg( 'pagenum', '%#%' ),
'format' => '',
'prev_text' => __( '«', 'aag' ),
'next_text' => __( '»', 'aag' ),
'total' => $num_of_pages,
'current' => $pagenum,
'add_args' => array( 'l_task' => $_POST['l_task'], 'l_val' => $_POST['l_val'] )
) );

Extending Drupal 7 search

I want to extend default Drupal 7 node search with one additional field.
I alter search form with the following new field:
function mymodule_form_search_form_alter(&$form, &$form_state, $form_id) {
$form['basic']['site'] = array(
'#type' => 'select',
'#options' => array(
'KEY1' => 'TITLE1',
'KEY2' => 'TITLE2',
'KEY3' => 'TITLE3'
)
);
}
I have a field called field_data_field_site.field_site_value which i need to use as a filter in this search.
I've tried to read about hook_search_* functions but didn't get the idea.
My question is the following. How can I extend search form? Anyone have live examples?
The following is the best way I solve this problem.
First of all I need to alter Drupal's search block and search form with my field and define new submit function.
/**
* Implements hook_form_FORM_ID_alter().
*/
function mymodule_form_search_block_form_alter(&$form, &$form_state, $form_id) {
$form['#submit'][] = 'search_form_alter_submit';
$form['site'] = array(
'#type' => 'select',
'#options' => _options(),
'#default_value' => (($_GET['site']) ? $_GET['site'] : '')
);
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function mymodule_form_search_form_alter(&$form, &$form_state, $form_id) {
$form['#submit'][] = 'search_form_alter_submit';
$form['basic']['site'] = array(
'#type' => 'select',
'#options' => _options(),
'#default_value' => (($_GET['site']) ? $_GET['site'] : '')
);
}
function _options() {
return array(
'' => 'Select site',
'site-1' => 'Site 1',
'site-2' => 'Site 2'
);
}
Submit function will forward us to default search/node page but with our query. Page would look like search/node/Our-query-string?site=Our-option-selected.
function search_form_alter_submit($form, &$form_state) {
$path = $form_state['redirect'];
$options = array(
'query' => array(
'site' => $form_state['values']['site']
)
);
drupal_goto($path, $options);
}
Next step is to use hook_search_info (Don't forget to turn it on and set as default on admin/config/search/settings page).
/**
* Implements hook_search_info().
*/
function mymodule_search_info() {
return array(
'title' => 'Content',
'path' => 'node',
'conditions_callback' => '_conditions_callback',
);
}
Conditions callback function defined in hook_search_info. We need to provide additional queries to our search.
function _conditions_callback($keys) {
$conditions = array();
if (!empty($_REQUEST['site'])) {
$conditions['site'] = $_REQUEST['site'];
}
return $conditions;
}
Finally, hook_search_execute will filter our content by our query. I used default code from this hook with modifications I need.
/**
* Implements hook_search_execute().
*/
function mymodule_search_execute($keys = NULL, $conditions = NULL) {
// Build matching conditions
$query = db_select('search_index', 'i', array('target' => 'slave'))
->extend('SearchQuery')
->extend('PagerDefault');
$query->join('node', 'n', 'n.nid = i.sid');
// Here goes my filter where I joined another table and
// filter by required field
$site = (isset($conditions['site'])) ? $conditions['site'] : NULL;
if ($site) {
$query->leftJoin('field_data_field_site', 's', 's.entity_id = i.sid');
$query->condition('s.field_site_value', $site);
}
// End of my filter
$query
->condition('n.status', 1)
->addTag('node_access')
->searchExpression($keys, 'node');
// Insert special keywords.
$query->setOption('type', 'n.type');
$query->setOption('language', 'n.language');
if ($query->setOption('term', 'ti.tid')) {
$query->join('taxonomy_index', 'ti', 'n.nid = ti.nid');
}
// Only continue if the first pass query matches.
if (!$query->executeFirstPass()) {
return array();
}
// Add the ranking expressions.
_node_rankings($query);
// Load results.
$find = $query
->limit(10)
->execute();
$results = array();
foreach ($find as $item) {
// Build the node body.
$node = node_load($item->sid);
node_build_content($node, 'search_result');
$node->body = drupal_render($node->content);
// Fetch comments for snippet.
$node->rendered .= ' ' . module_invoke('comment', 'node_update_index', $node);
// Fetch terms for snippet.
$node->rendered .= ' ' . module_invoke('taxonomy', 'node_update_index', $node);
$extra = module_invoke_all('node_search_result', $node);
$results[] = array(
'link' => url("node/{$item->sid}", array('absolute' => TRUE)),
'type' => check_plain(node_type_get_name($node)),
'title' => $node->title,
'user' => theme('username', array('account' => $node)),
'date' => $node->changed,
'node' => $node,
'extra' => $extra,
'score' => $item->calculated_score,
'snippet' => search_excerpt($keys, $node->body)
);
}
return $results;
}
I'd be happy if anyone would give me a better answer.

Module development: hook_form(), hook_menu() and how to get $_POST

I want to improve my knowledge in module development (which is far away from basic), so I try to develop a perimeter search module.
What I've achieved for now is a block containing a form:
function perimeter_search_block_view($delta = '') {
// Define an empty array for the block output.
$block = array();
switch($delta) {
case 'perimeter_search_box':
$block['subject'] = t('Perimeter search box');
$block['content'] = drupal_get_form('perimeter_search_form');;
break;
}
return $block;
}
/**
* Implementation of the perimeter search form
* #return array with form data
*/
function perimeter_search_form($form, &$form_state) {
$form = array(
'#action' => 'perimeter-search-results',
'keyword' => array(
'#type' => 'textfield'
),
'location' => array(
'#type' => 'textfield'
),
'perimeter' => array(
'#type' => 'select',
'#title' => t('Perimeter'),
'#options' => array('15 km', '30 km', '60 km', '120 km')
),
'submit' => array(
'#type' => 'submit',
'#value' => t('Start search')
)
);
return $form;
}
I also have a function to output the search results:
/**
* Implementation of hook_menu()
* #return defined menu/page items
*/
function perimeter_search_menu() {
$items = array();
// Search results page
$items['perimeter-search-results'] = array(
'title' => t('Perimeter search results'),
'page callback' => 'perimeter_search_results',
'access arguments' => array('view perimeter search'),
'type' => MENU_NORMAL_ITEM
);
return $items;
}
/**
* Processing job search queries
*/
function perimeter_search_results() {
$page_content = t('Search results');
return $page_content;
}
My (simple?) question is: how to get the post data (keyword, location, perimeter) in my perimeter_search_results() function?
Easy, you have to create the _submit function for your form, here an example:
function perimeter_search_form_submit($form, &$form_state) {
/*
* Your data handling goes here on the $form_state['values']['myfieldname']
* variable.
*/
drupal_set_message(t('Awesome, you managed to fill the form!'));
}
And if you need to validate..
function perimeter_search_form_validate($form, &$form_state) {
if($form_state['values'['myfieldname'] == '') {
form_set_error('', t('Hey, it doesn't work like that!'));
}
}
Just remember that if you add the attribute '#required' => TRUE to a form field, the field will be automatically validated to always require that field, so you don't need to use the validator for that field if you just need that it get compiled.

Resources