Eloquent query, combine where and whereBetween - eloquent

Is there any wrong with this eloquent query? I want to use a where and wherebetween. I dont get any items in return.
$start_date = "2021-02-1-01";
$end_date = "2021-02-28";
$date_array = array($start_date, $end_date);
$data = array(
'project_id' => $project_id,
);
return WasteDataModel::where($data)
->where(function ($query) use($date_array) {
return $query->whereBetween('date', $date_array);
})->get();
I have tried this aswell
return WasteDataModel::where($data)->whereBetween('date', $date_array)->get();
with no return

Use DATE method of MYSQL with DB::raw() to compare dates only
return WasteDataModel::where($data)
->whereBetween(DB::raw('DATE(date)'), $date_array)
->get();

Related

Laravel Restful API - How to create custom query based on multiples params

I am new to Laravel. I have created a Restful API with this route : (with Laravel 5)
Route::get('api/clubs', 'Api\ClubsController#getClubs');
Calling /public/api/clubs will retrieve all clubs
$clubs = Club::all();
With the same route I am trying to filter clubs based on many params :
$filter_lat = Request::get( 'fi_lat' );
$filter_long = Request::get( 'fi_long' );
$filter_distance = Request::get( 'fi_distance' );
$filter_key_word = Request::get( 'fi_key_word' );
$filter_date = Request::get( 'fi_date' );
$filter_city_id = Request::get( 'fi_city_id' );
$filter_order_by = Request::get( 'fi_order_by' );
$filter_offset = Request::get( 'fi_offset' );
$filter_limit = Request::get( 'fi_limit' );
I want to filter my clubs based on those params, but I don't know what is the best way to build the query using Eloquent. I want to be able to run the query even if I have only 1 param. example1 : I want to filter my clubs by city and my query will looks like :
$clubs = Club::where( 'city_id', $filter_city_id )->get();
If I have more params my query will looks like :
$clubs = Club::where( condition 1 )
->where( condition 2 )
->where( condition 3 )
etc ..
->get();
How to build my query this way and put together all conditions ?
if( !empty ( $my_param ) ){
$query += where ( condition .. )
}
then run $query ..
What I don't want to do, is doing 10 "if() tests" and build 10 queries depending on params because this will be really a big code hacking and duplicated query ..
I am open to any suggestions, I want to have an object of clubs as result. Thanks!
It's quite simple, just check that each condition was passed and if it did, add a where clause.
$query = Club::newQuery();
if($my_param) {
$query->where('my_param', $my_param);
}
if($my_param2) {
$query->where('my_param2', $my_param2);
}
$clubs = $query->get();
You should check that the variable is set simply because it's good practice when dealing with user submitted data. If there are some parameters which you need to validate further, be sure to use the validation class.
You can use array inside where() . like this:
User::where(['id' => $id, 'name' => $name, 'etc' => $etc])->get();
:)...

How to prevent SQL injection in PhalconPHP when using sql in model?

Let's say I am building a search that finds all the teacher and got an input where the user can put in the search term. I tried reading the phalcon documentation but I only see things like binding parameters. I read the other thread about needing prepare statements do I need that in Phalcon as well?
And my function in the model would be something like this:
public function findTeachers($q, $userId, $isUser, $page, $limit, $sort)
{
$sql = 'SELECT id FROM tags WHERE name LIKE "%' . $q . '%"';
$result = new Resultset(null, $this,
$this->getReadConnection()->query($sql, array()));
$tagResult = $result->toArray();
$tagList = array();
foreach ($tagResult as $key => $value) {
$tagList[] = $value['id'];
....
}
}
My question is for the Phalcon framework is there any settings or formats I should code for this line $sql = 'SELECT id FROM tags WHERE name LIKE "%' . $q . '%"';
And also any general recommendation for preventing SQL Injection in PhalconPHP controllers and index would be appreciated.
For reference:
My controller:
public function searchAction()
{
$this->view->disable();
$q = $this->request->get("q");
$sort = $this->request->get("sort");
$searchUserModel = new SearchUsers();
$loginUser = $this->component->user->getSessionUser();
if (!$loginUser) {
$loginUser = new stdClass;
$loginUser->id = '';
}
$page = $this->request->get("page");
$limit = 2;
if (!$page){
$page = 1;
}
$list = $searchUserModel->findTeachers($q, $loginUser->id, ($loginUser->id)?true:false, $page, $limit, $sort);
if ($list){
$list['status'] = true;
}
echo json_encode($list);
}
My Ajax:
function(cb){
$.ajax({
url: '/search/search?q=' + mapObject.q + '&sort=<?php echo $sort;?>' + '&page=' + mapObject.page,
data:{},
success: function(res) {
//console.log(res);
var result = JSON.parse(res);
if (!result.status){
return cb(null, result.list);
}else{
return cb(null, []);
}
},
error: function(xhr, ajaxOptions, thrownError) {
cb(null, []);
}
});
with q being the user's search term.
You should bind the query parameter to avoid an SQL injection. From what I can remember Phalcon can be a bit funny with putting the '%' wildcard in the conditions value so I put them in the bind.
This would be better than just filtering the query.
$tags = Tags::find([
'conditions' => 'name LIKE :name:',
'bind' => [
'name' => "%" . $q . "%"
]
])
Phalcon\Filter is helpful when interacting with the database.
In your controller you can say, remove everything except letters and numbers from $q.
$q = $this->request->get("q");
$q = $this->filter->sanitize($q, 'alphanum');
The shortest way for requests:
$q = $this->request->get('q', 'alphanum');

merge two Where with AND in zend 2

I have problem to merge two conditions in my query
public function GetInbox($user_id , $line_number = false , $seeall = false , $limit = 0,$SearchWhere = null)
{
if(!$SearchWhere)
$SearchWhere = new Where();
if(!$seeall)
{
$UsersLineTable = new UsersLinesTable($this->adapter);
$UsersLine = $UsersLineTable->fetchAll(array('user_id = ?' => $user_id,'owner_type = ?' => '1'));
if(!$UsersLine) return false;
$SearchWhere2 = new Where();
foreach ($UsersLine as $key => $value) {
$SearchWhere2->equalTo("recipient_number",$value['line_number'])
->or
->equalTo("recipient_number",'98'.$value['line_number'])
->or;
}
$Select = new Select();
$Select->where($SearchWhere2);
$Select->where($SearchWhere);
if($limit)
$Select->limit($limit);
$Select->order("receive_date DESC");
$MessageProvidersInboxTable = new MessageProvidersInboxTable($this->adapter);
return $MessageProvidersInboxTable->fetchBySelect($Select);
}else{
$MessageProvidersInboxTable = new MessageProvidersInboxTable($this->adapter);
return $MessageProvidersInboxTable->fetchAll($SearchWhere);
}
}
$SearchWhere is a Where class,
$SearchWhere2 is second conditions
In this case
$Select->where($SearchWhere2);
$Select->where($SearchWhere);
$select just contain $SearchWhere conditions.
I want this query
Where condition1 AND (condition2)
is that important that condition2 contain conditions include OR operand.
Sincerely
Try this, it should work. Writing without looking at zend\db code:
$where = new \Zend\Db\Sql\Where;
$where->addPredicate($SearchWhere, $where::OP_AND);
$where->addPredicate($SearchWhere2, $where::OP_AND);
$select->where($where);
Basically, Where object is a subclass of Predicate. So it should work as any other predicate.

Symfony Zend Lucene Search Multiple Tables

I have a Symfony project and I used Zend Lucene Search framework to integrate a search on the site. It works beautifully but it's limited to searching 1 table.
I need my users to be able to search the whole site (8 select tables) and return the results all together. Each table has the same fields indexed. This is the code that specifies the table and calls the query.
Is there a way to make it look through all 8 tables for results?
public function getForLuceneQuery($query)
{
$hits = self::getLuceneIndex()->find($query);
$pks = array();
foreach ($hits as $hit)
{
$pks[] = $hit->pk;
}
if (empty($pks))
{
return array();
}
$alltables = Doctrine_Core::getTable('Car');
$q = $alltables->createQuery('j')
->whereIn('j.token', $pks)
->orderBy('j.endtime ASC')
->andwhere('j.endtime > ?', date('Y-m-d H:i:s', time()))
->andWhere('j.activated = ?', '1')
->limit(21);
return $q->execute();
}
To give a bit of background on the 8 tables, they are all basically similar. They all have title, make, model, etc so I need to run a single query on all of them and return all results (regardless of which table it is in) in Ascending order. The Doctrine_core::getTable command doesn't seem to like multiple tables or even arrays (unless I'm not doing it right). Thanks!
UPDATE (WORKING):
Here is the updated code. This is what I have in the SearchTable.class.php file:
public function getForLuceneQuery($query)
{
// sort search result by end time
$hits = self::getLuceneIndex()->find(
$query, 'endtime', SORT_NUMERIC, SORT_ASC
);
$result = array(
'index' => $hits,
'database' => array(),
);
// group search result by class
foreach ($hits as $hit)
{
if (!isset($result['database'][$hit->class]))
{
$result['database'][$hit->class] = array();
}
$result['database'][$hit->class][] = $hit->pk;
}
// replace primary keys with real results
foreach ($result['database'] as $class => $pks)
{
$result['database'][$class] = Doctrine_Query::create()
// important to INDEXBY the same field as $hit->pk
->from($class . ' j INDEXBY j.token')
->whereIn('j.token', $pks)
->orderBy('j.endtime ASC')
->andwhere('j.endtime > ?', date('Y-m-d H:i:s', time()))
->andWhere('j.activated = ?', '1')
->limit(21)
->execute();
}
return $result;
}
Here is what I have in the actions.class.php file for the Search Module:
public function executeIndex(sfWebRequest $request)
{
$this->forwardUnless($query = $request->getParameter('query'), 'home', 'index');
$this->results = Doctrine_Core::getTable('Search')
->getForLuceneQuery($query);
}
And finally this is my template file indexSuccess.php I have simplified it so it's easier to understand. My indexSuccess.php is more complicated but now that I can call the values, I can customize it further.
<div class="product_list"
<ul>
<?php foreach ($results['index'] as $hit): ?>
<li class="item">
<?php if (isset($results['database'][$hit->class][$hit->pk])) ?>
<span class="title">
<?php echo $results['database'][$hit->class][$hit->pk]->getTitle() ?>
</span>
</li>
<?php endforeach ?>
</ul>
</div>
This works beautifully. I was able to customize it by calling each of the fields in the search results and it works perfect. I added an item to each of the tables with the same title and the search result pulled them all. Thank you so much!
OK. I'll try to give you some hint, with code :)
First of all you should add these fields to the index:
$doc->addField(Zend_Search_Lucene_Field::Keyword('class', get_class($record)));
$doc->addField(Zend_Search_Lucene_Field::UnIndexed('endtime', strtotime($record->get('endtime'))));
Than you should use these new fields:
public function getForLuceneQuery($query)
{
// sort search result by end time
$hits = self::getLuceneIndex()->find(
$query, 'endtime', SORT_NUMERIC, SORT_ASC
);
$result = array(
'index' => $hits,
'database' => array(),
);
// group search result by class
foreach ($hits as $hit)
{
if (!isset($result['database'][$hit->class]))
{
$result['database'][$hit->class] = array();
}
$result['database'][$hit->class][] = $hit->pk;
}
// replace primary keys with real results
foreach ($result['database'] as $class => $pks)
{
$result['database'][$class] = Doctrine_Query::create()
// important to INDEXBY the same field as $hit->pk
->from($class . ' j INDEXBY j.token')
->whereIn('j.token', $pks)
->orderBy('j.endtime ASC')
->andwhere('j.endtime > ?', date('Y-m-d H:i:s', time()))
->andWhere('j.activated = ?', '1')
->limit(21)
->execute();
// if you want different query per table
// you should call a function which executes the query
//
// if (!method_exists($table = Doctrine_Core::getTable($class), 'getLuceneSearchResult'))
// {
// throw new RuntimeException(sprintf('"%s::%s" have to be exists to get the search results.', get_class($table), 'getLuceneSearchResult'));
// }
//
// $results[$class] = call_user_func(array($table, 'getLuceneSearchResult'), $pks);
}
return $result;
}
After that in the template you should iterate over $result['index'] and display results from $result['database']
foreach ($result['index'] as $hit)
{
if (isset($result['database'][$hit->class][$hit->pk]))
{
echo $result['database'][$hit->class][$hit->pk];
}
}
And there are same alternate (maybe better) solutions that I can think of:
Alternate solution #1:
You can store data in the index and this data will be accessible in the search result. If you not need too much data when displaying the results and can update the index frequently I think this is a good option. This way you can use pagination and no SQL queries needed at all.
$doc->addField(Zend_Search_Lucene_Field::Text('title', $content->get('title')));
...
$hit->title;
Alternate solution #2:
As you wrote, your tables are very similar, so you maybe could use column aggregation inheritance. In this way all data stored in one table so you can query them all together and can order and paginate as you want.

How do I do this sql query in Zend?

How do I do this sql query in Zend Framework, I need to some how do this in the PDO context I think? I tried ->query but not sure if I am getting this right. The three variables are user_id and to and from date.
SELECT
ss.subcategory_id,
ss.subcategory_name,
ss.subcategory_issaving,
IFNULL(SUM(m.mv_monthly_total),0) AS expendsum
FROM
(SELECT
s.subcategory_id,
s.subcategory_name,
s.subcategory_issaving
FROM
subcategory s
WHERE
s.subcategory_isexpend = 'Y'
AND
s.subcategory_issaving = 'Y') ss
LEFT JOIN
mv_monthly m
ON ss.subcategory_id = m.mv_monthly_subcategory_id
AND m.mv_monthly_user_id = 2
AND m.mv_monthly_month >= '2010-01-01'
AND m.mv_monthly_month <= '2020-01-01'
GROUP BY
ss.subcategory_id,
ss.subcategory_name,
ss.subcategory_issaving
ORDER BY
ss.subcategory_issaving DESC,
expendsum;
I have tried the following with no luck
$db = Zend_Db_Table::getDefaultAdapter();
$dbExpr1 = new Zend_Db_Expr("s.subcategory_id, s.subcategory_name, s.subcategory_issaving");
$dbExpr2 = new Zend_Db_Expr("ss.subcategory_id, ss.subcategory_name, ss.subcategory_issaving, IFNULL(SUM(m.mv_monthly_total),0) AS expendsum");
$select = $db->select()
->from(
array(
'ss' => new Zend_Db_Expr(
'('. $db->select()
->from(array("s" => "subcategory"), $dbExpr1)
->where("s.subcategory_isexpend = 'Y'")
->where("s.subcategory_issaving = 'Y'") .')'
)
),
$dbExpr2
)
->joinLeft(array("m" => "mv_monthly"), "ss.subcategory_id = m.mv_monthly_subcategory_id")
->where("m.mv_monthly_user_id = ?", $user_id)
->where("m.mv_monthly_month >= ?", $fromMonth)
->where("m.mv_monthly_month <= ?", $toMonth)
->group(array("ss.subcategory_id","ss.subcategory_name","ss.subcategory_issaving"))
->order(array("ss.subcategory_issaving DESC", "expendsum"));
$row = $db->fetchAll($select);
For such a complex query, you can just execute it directly rather than using the object oriented approach as it gets fairly complicated with a query like that.
Try something like this, replacing my query with yours, and binding your variables into the query:
$db = Zend_Db_Table::getDefaultAdapter();
$stmt = new Zend_Db_Statement_Pdo($db, 'SELECT a, b, c FROM a WHERE username = ? AND date = ?');
try {
$res = $stmt->execute(array($user_id, $fromMonth));
if ($res) {
$rows = $stmt->fetchAll();
}
} catch (Zend_Db_Statement_Exception $dbex) {
// log Query failed with exception $dbex->getMessage();
}
If you prefer to use the object oriented approach, or need to because some parts of the query will be conditional, I usually build by subqueries up first as their own select, and you can simply embed those in to the main query with the select object for the subquery.
Here is what I mean by that:
$subselect = $this->getDbTable()
->select()
->from('mytable', array('time' => 'max(time)', 'id'))
->where('id IN (?)', $serialNumbers)
->group('id');
$select = $this->getDbTable()
->select()
->setIntegrityCheck(false)
->from('mytable')
->join('other', 'mytable.id = other.id', array('label'))
->join(array('dt' => $subselect),
'(mytable.time, mytable.id) = (dt.time, dt.id)', '');