Query Builder using the like operator and dynamic values - cakephp-3.0

CakePHP Version 3.5.5
What I've got:
I've got search functionality on my index pages which allows a user to search by column and value. The user selects the column from a select list and adds text into an input. I pick up this data with the following which works:
$query = $Users->find()
->where(function ($exp, $q) {
return $exp->like($this->request->getData('column'), $this->request->getData('input') . '%');
})
->andWhere([
'status' => $filter,
'cid_1' => $c1
]);
When using the debugKit it reveals the sql as: (Extract only to help explain)
FROM users Users WHERE (role LIKE :c0 AND status = :c1 AND cid_1 = :c2)',
What I'm trying to do is the following:
$testColumn = $this->request->getData('column');
$testInput = $this->request->getData('input');
$query = $Users->find()
->where(function ($exp, $q) {
return $exp->like($testColumn, $testInput . '%');
})
->andWhere([
'status' => $filter,
'cid_1' => $c1
]);
The $testColumn variable is undefined.
Whe using the debugKit it reveals the sql as: (Extract only to help explain)
FROM users Users WHERE ( LIKE :c0 AND status = :c1 AND cid_1 = :c2)',
IE: The role is not being declared before the LIKE.
What I've tried:
1. return $exp->like("$testColumn", "$testInput" . '%');
Result: Exactly the same - Variable is still undefined.
DebugKit: FROM users Users WHERE ( LIKE :c0 AND status = :c1 AND cid_1 = :c2)',
2. return $exp->like("'$testColumn'", "'$testInput'" . '%');
Result: It added '' before LIKE as can be seen below but I still can't get that variable defined.
DebugKit: FROM users Users WHERE ('' LIKE :c0 AND status = :c1 AND cid_1 = :c2)',
My Question:
Is there a way to use a dynamic value to select the search data.
Update:
Is it that I can't assign getData to a variable in this context.
You can't do this:
$testColumn = $this->request->getData('column');
return $exp->like($testColumn, $testInput . '%');
But you can do this:
$testColumn = $this->request->getData('column');
echo 'testColumn is ' . $testColumn . '<br />';
if ($testColumn === 'role') {
echo 'in column passed ' . '<br />';
}
else {
echo 'in column failed ' . '<br />';
}
Thanks. Z.
////////////////////////////////////////////////////////////////////////////////
Alimon Karim as requested.
I'm using post and my url is: https://localhost/app/users/search
Thanks Alimon, it works.

Replace
->where(function ($exp, $q) {
with
->where(function ($exp, $q) use ($testColumn,$testInput) {

Related

Unique Profile Slug with PHP and PDO

I am using a class to generate a string name profile to slug and next use an SQL command to tell me whats the unique value to use in insert command, the problem is the command isn't working properly, sometimes it is possible to return a value which already exist...
Thats the class I am using to generate the slug: (composer require channaveer/slug)
And this the example code:
use Channaveer\Slug\Slug;
$string = "john doe";
$slug = Slug::create($string);
$profile_count_stmt = $pdo->prepare("
SELECT
COUNT(`id`) slug_count
FROM
`advogados_e_escritorios`
WHERE
`slug_perfil` LIKE :slug
");
$profile_count_stmt->execute([
":slug" => "%".$slug."%"
]);
$profile_count = $profile_count_stmt->fetchObject();
if ($profile_count && $profile_count->slug_count > 0) {
$profile_increment = $profile_count->slug_count + 1;
$slug = $slug . '-' . $profile_increment;
}
echo 'Your unique slug: '. $slug;
// Your unique slug: john-doe-5
This is the content of the table when the script run:
Do you know how can I improve the select command to prevent it to return existing slugs from DB?
Ok finally found a solution... Heres the code for who wants to generate unique profile slugs using PHP - PDO and MySQL
$string = "John Doe";
$string = mb_strtolower(preg_replace('/\s+/', '-', $string));
$slug = iconv('UTF-8', 'ASCII//TRANSLIT', $string);
$pdo = Conectar();
$sql = "
SELECT slug_perfil
FROM advogados_e_escritorios
WHERE slug_perfil
LIKE '$slug%'
";
$statement = $pdo->prepare($sql);
if($statement->execute())
{
$total_row = $statement->rowCount();
if($total_row > 0)
{
$result = $statement->fetchAll();
foreach($result as $row)
{
$data[] = $row['slug_perfil'];
}
if(in_array($slug, $data))
{
$count = 0;
while( in_array( ($slug . '-' . ++$count ), $data) );
$slug = $slug . '-' . $count;
}
}
}
echo $slug;
//john-doe-1
You should check if the slug exists or not from your database. If it already exists then you can append some random string like the following
$slug = Slug::create($string);
$slugExists = "DB query to check if the slug exists in your database then you may return the count of rows";
//If the count of rows is more than 0, then add some random string
if($slugExists) {
/** NOTE: you can use primary key - id to append after the slug, but that has to be done after you create the user record. This will help you to achieve the concurrency problem as #YourCommenSense was stating. */
$slug = $slug.time(); //time() function will return time in number of seconds
}
//DB query to insert into database
I have followed the same for my blog articles (StackCoder) too. Even LinkedIn follows the same fashion.
Following is screenshot from LinkedIn URL

Search multiple words separated by space in laravel 6 doesn't work

I've read several similar posts and I tried them, but still it doesn't work for my site.
When I search a word "think" the search result shows "I think this is it".
However, when I search "I think" the result is "0 match found".
So to achieve multiple words search, I did below:
Controller.php
$keyword = "I think"
$words = explode(' ', $keyword);
$data = Post::where(function ($query) use ($words)
{
foreach($words as $word){
$query->where('example', 'LIKE', '%' . $word . '%');
}
})->get();
However, result is same. "think" shows results. "I think" doesn't hit any results.
also debugged with dd($data)
// $keyword = "think"
"select * from `posts` where (`example` LIKE ?)"
// $keyword = "I think"
"select * from `posts` where (`example` LIKE ? and `example` LIKE ?)"
So what would be wrong? Thank you.
[Additonal info (Edited)]
$bindings = $query->getBindings();
dd($bindings);
// array:2 [▼
0 => "%I%"
1 => "%think%"
]
I would suggest using Laravel scout for this. But alternatively, to do a fuzzy text search in sql.
Or... assuming we don't use MYSQL match features, and we were to use a laravel eloquent.
1) Split the string.
2) use query builder.
$words = explode(' ', $searchString);
$results = Post::where(($q) use ($words) {
$firstWord = array_shift($words);
$q->where('example', $firstCase);
foreach($words as $word) {
$q->orWhere('example', $word);
}
})->get();
SOLVED
It was my bad.
On my blade.php, there was a line
#if (preg_match("/^[0-9a-zA-Z]*$/",$keyword))
this regex didn't have space, that's why it didn't work.
so just changed to:
#if (preg_match("/^[0-9a-zA-Z ]*$/",$keyword))
then totally it worked.
Thank you for helping teachers.
$users = User::where(function ($query) use($userSearch) {
$searchWords = explode(' ', $userSearch);
$query->where('name', 'like', '%'.$searchWords[0].'%');
for($i=1;$i<count($searchWords);$i++) {
$query->where('name', 'like', '%'.$searchWords[$i].'%');
}
})->get();

Using DataTables to a table with content from ajax

I am working on this program. The program displays the topics under a specific subject.
My main question is, will the DataTable work in this kind of setup?
This is the code in HTML.
<div class="table-responsive" id="subject_container">
<table id="tbl_subject" class="table table-striped">
<thead>
<tr>
<th>Chapter</th>
<th>Topic</th>
<th>Content</th>
</tr>
</thead>
<tbody id="disp_topics"></tbody>
</table>
</div>
This is the code snippet in jquery:
$(function(){
//this is only for simplification. Subjectid will be coming from a select input.
let subjectid = 1;
get_topics(subjectid);
//this part right here. If this cannot work, is there a way to make it work?
$('#tbl_subject).DataTable();
});
function get_topics(subjectid){
$.ajax({
url: 'includes/subject_handler.php',
method: 'POST',
data: {
key: 'get_topics',
subjectid: subjectid
},
success: function(data){
$('#disp_topics).html(data);
}
});
This is the code of includes/subject_handler.php:
I am using keys since there are other tasks handled by this same script file. Thank you for understanding.
if($_POST['key'] == 'get_topics'){
$subjectid = $_POST['subjectid'];
$data = '';
if(!empty($subjectid)){
$sql = "SELECT topic.subjectid, chapter, topic, content FROM topic INNER JOIN subject
ON subject.subjectid=topic.subjectid WHERE topic.subjectid = ?";
$stmt=$db->prepare($sql);
$stmt->bindValue(1, $subjectid);
$stmt->execute();
if($stmt->rowCount() > 0){
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach($rows as $row){
$data .= '<tr>
<td>'.$row['chapter'].'</td>
<td>'.$row['topic'].'</td>
<td>'.$row['content'].'</td>
</tr>';
}
exit($data);
}
}
}
you can use ajax attribute of data table.
$('#tbl_subject').DataTable( {
processing: true,
serverSide: true,
serverMethod: 'post',
ajax: 'includes/subject_handler.php'
columns: [
{data: 'chapter', name: 'chapter'},
{data: 'topic', name: 'topic'},
{data: 'content', name: 'content'},
]
} );
And for backend if you need to access search value you can do like this,
$requestData = $_REQUEST;
$requestData['search']['value']
In your case this is how to use it in where close.
$sql .= " AND ( chapter LIKE '" . $requestData['search']['value'] . "%' ";
$sql .= " OR topic LIKE '" . $requestData['search']['value'] . "%' ";
$sql .= " OR content LIKE '" . $requestData['search']['value'] . "%' )";
And if you need to get sorting data you can get like this,
$requestData['order'][0]['column'] // column name which user sorts
$requestData['order'][0]['dir'] // derection (ASC/DESC)
In your case it will be like this and remember to use $requestData['start'] and $requestData['length'] for pagination to work.
$sql .= " ORDER BY " . $columns[$requestData['order'][0]['column']] . " " . $requestData['order'][0]['dir'] . " LIMIT " . $requestData['start'] . " ," . $requestData['length'] . " ";
You can include this search value and order values inside your sql query inside backend.
And finally you need format the data before send to front end.
$json_data = array(
"draw" => intval($requestData['draw']), // for every request/draw by clientside , they send a number as a parameter, when they recieve a response/data they first check the draw number, so we are sending same number in draw.
"recordsTotal" => intval($totalData), // total number of records
"recordsFiltered" => intval($totalFiltered), // total number of records after searching, if there is no searching then totalFiltered = totalData
"data" => $data // total data array
);
echo json_encode($json_data);
This is much simpler than looks. Need to get total records count from query, and total filtered record count from another query and dataset in json.
I have added previous sql example only to explain. For preventing SQL injection you have to use Prepared Statements.
$sql .= " AND ( chapter LIKE ? OR topic LIKE ? OR content LIKE ?";
$params = array($requestData['search']['value'] . "%", $requestData['search']['value'] . "%", $requestData['search']['value'] . "%");
$stmt = $handle->prepare($sql);
$stmt->execute($params);
And you need to do the same for ORDER BY and LIMIT part of the query.
and inside this tutorial there is a nice example how it works on backend.
You can send data to back end also like this.
ajax: {
url: "Your url",
data: function (d) {
d.var1= 'var 1';
d.var2= 'var 2';
}
},
And access the data in back end like this.
$var1 = $_REQUEST['var1'];
$var2 = $_REQUEST['var2'];
And be careful not to use start and end (E.g.:- d.start= 'var 1';) to pass data to back end since they are used for pagination. This link has a nice example on this.

codeigniter active records join with using?

I am wanting to write this query:
SELECT u.userId, uil.interestId, url.regionId FROM users u
JOIN userInterestLink uil USING (userId)
JOIN userRegionLink url USING (userId)
JOIN dealInterestLink dil USING (interestId)
JOIN dealRegionLink drl USING (regionId, dealId)
WHERE dealId = 1
using the code igniter active records class, however I have no ideda how to do the JOIN ... USING
There is no built-in support for JOIN ... USING in the active record class. Your best bet would probably change the join() function to be like this (the file is system/database/DB_active_rec.php in case you don't know)
public function join($table, $cond, $type = '')
{
if ($type != '')
{
$type = strtoupper(trim($type));
if ( ! in_array($type, array('LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER')))
{
$type = '';
}
else
{
$type .= ' ';
}
}
// Extract any aliases that might exist. We use this information
// in the _protect_identifiers to know whether to add a table prefix
$this->_track_aliases($table);
// Strip apart the condition and protect the identifiers
if (preg_match('/([\w\.]+)([\W\s]+)(.+)/', $cond, $match))
{
$match[1] = $this->_protect_identifiers($match[1]);
$match[3] = $this->_protect_identifiers($match[3]);
$cond = $match[1].$match[2].$match[3];
}
// Assemble the JOIN statement
$type.'JOIN '.$this->_protect_identifiers($table, TRUE, NULL, FALSE);
$using_match = preg_match('/using[ (]/i', $cond);
if ($using_match)
{
$join .= $cond;
}
else
{
$join .= ' ON '.$cond;
}
$this->ar_join[] = $join;
if ($this->ar_caching === TRUE)
{
$this->ar_cache_join[] = $join;
$this->ar_cache_exists[] = 'join';
}
return $this;
}
So then, you can simply use this in your code join('table', 'USING ("something")')
Although, you might want to extend the class instead of modifying it so that you won't need to do same thing over and over again when you upgrade your CI.
Have a look at this article or this one (or search google) if you want to do so instead.
Or if you don't want to go all those troubles, you can write a simple helper function that can do the same thing.
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
function join_using($table, $key)
{
$CI = get_instance();
$join = 'JOIN '. $table .' USING (`'. $key .'`)';
return $CI->db->ar_join[] = $join;
}
Later on, just load the helper and call the function like this join_using('table', 'key'). It will then produce the same result as you would with the original join() except this one will give you USING instead of ON condition.
For example:
// $something1 and $something2 will produce the same result.
$something1 = $this->db->join('join_table', 'join_table.id = table.id')->get('table')->result();
print_r($something1);
join_using('join_table', 'id');
$something2 = $this->db->get('table')->result();
print_r($something2);

Wordpress Shortcode that query some value from MySql

I've a MySql table where I put some value: id, name of opportunity, category of opportunity, commission etc etc. Now I need to create (automatically) a shortcode that call these value win an array, so for example if i write [opportunity id="1"] wordpress display banner of the opportunity in the database that have id=1.
This is my code
function opportunity_banner_shortcode($atts) {
extract(shortcode_atts(array("id" => ''), $atts));
global $table_prefix, $wpdb, $user_level;
$table_name = $table_prefix . "opportunities";
$finds = $wpdb->get_results("SELECT * FROM {$table_name}", ARRAY_A);
if(sizeof($finds)){
foreach($finds as $find)
return "<a href='" . $find["opp_link"].
"'><img src='" . $find["opp_banner_preview"]."'></a> ";
}
}
add_shortcode('opportunity', 'opportunity_banner_shortcode');
Thanks to all
Maybe the query should be
$finds = $wpdb->get_results("SELECT * FROM {$table_name} WHERE id={$id}",
ARRAY_A);