Add 'where' in query with foreach or something similar - mysql

this is my problem.
product_field_glue table:
id | field_id | value
----------------------
1 | 50 | blue
2 | 51 | wood
3 | 50 | red
4 | 35 | car
I need to search products by this fields according _GET params
/?field[50][]=blue&field[35][]=car
this should return all blue cars
$result = Products::select('products.*')
->leftJoin('product_field_glue', function ($join) {
$join->on('product_field_glue.product_code', '=', 'products.product_code');
})
->where(function($query) use ($id, $value){
$query->where('product_fields.field_id', $id);
$query->where('product_fields.value', $value);
})
This is modified (part of) query. As you can see I need to set 'where' for all _GET['field'] and $key=>$value of GET parameter need to be in grouped where.
Maybe there is better solution for this kind of search which you can recommend.

I like to use scopes for this.
Inside your Products class:
/**
* Warning: requires a join on product_field_glue table
*
* #param \Illuminate\Database\Eloquent\Builder $query
* #param array $fields
* #return \Illuminate\Database\Eloquent\Builder
*/
public function scopeSearchFields($query, $fields) {
if (empty($fields)) {
return $query;
}
foreach ($fields as $id => $values) {
$query = $query->where(function ($subquery) use($id, $values) {
$subquery->where('product_field_glue.field_id', $id);
$subquery->whereIn('product_field_glue.value', $values);
});
}
return $query;
}
Notice that the method is called scopeSearchFields. You can attach it to any Products query as a chained searchFields method:
$result = Products::select('products.*')
->leftJoin('product_field_glue', function ($join) {
$join->on('product_field_glue.product_code', '=', 'products.product_code');
})
->searchFields($request->input('fields'))
->get();
The beauty of this is that it's extremely reusable and keeps all of the if and for loops inside of the scope method.

Related

Laravel: Have an existing query but need to apply it to only the last 30 days

I have a query:
User::selectRaw('users.facebook_id')
->join('orders', 'orders.customer_id', 'shop_users.id')
->groupBy('shop_users.id')
->havingRaw('SUM(orders.total) >= 0')
->get()->pluck('facebook_id')->all();
but I want it only for orders from the last 30 days from now.
I assume I could use something such as ->whereDate('created_at', '>', Carbon::now()->subDays(30)) but not sure how to apply this to orders.
I think where between is what you need
Here try this
User::selectRaw('users.facebook_id')
->join('orders', 'orders.customer_id', 'shop_users.id')
->groupBy('shop_users.id')
->havingRaw('SUM(orders.total) >= 0')
->whereBetween('orders.created_at', [Carbon::now()->addDays(-30), Carbon::now()])
->get()->pluck('facebook_id')->all();
This will work if your datatype is dateTime.
now if you want to date only you can use this ->toDateString() just add this on your carbon
Hope it helps.
Here is the Join function from Laravel Code base,
/**
* Add a join clause to the query.
*
* #param string $table
* #param \Closure|string $first
* #param string|null $operator
* #param string|null $second
* #param string $type
* #param bool $where
* #return $this
*/
public function join($table, $first, $operator = null, $second = null, $type = 'inner', $where = false)
{
$join = $this->newJoinClause($this, $type, $table);
// If the first "column" of the join is really a Closure instance the developer
// is trying to build a join with a complex "on" clause containing more than
// one condition, so we will add the join and call a Closure with the query.
if ($first instanceof Closure) {
call_user_func($first, $join);
$this->joins[] = $join;
$this->addBinding($join->getBindings(), 'join');
}
// If the column is simply a string, we can assume the join simply has a basic
// "on" clause with a single condition. So we will just build the join with
// this simple join clauses attached to it. There is not a join callback.
else {
$method = $where ? 'where' : 'on';
$this->joins[] = $join->$method($first, $operator, $second);
$this->addBinding($join->getBindings(), 'join');
}
return $this;
}
That means, you can do may be something like below
User::selectRaw('users.facebook_id')
->join('orders',function ($j) {
// Now $j is instance of builder e.g.
// $j->on('users.id', '=', 'orders.id')->orOn('users.name', '=', 'orders.name')->->whereDate('created_at', '>', Carbon::now()->subDays(30));
// Not super clear on the tables you are using
})
->groupBy('shop_users.id')
->havingRaw('SUM(orders.total) >= 0')
->get()->pluck('facebook_id')->all();

How to order_by from specific data

I have a database like this
order_id | date | qty
------------------------------------
a |2018-11-11 10:03:33 |1
b |2018-11-12 10:03:33 |1
c |2018-11-11 12:03:33 |1
and I have a model like this,
public function get_total_sales_per_day($date_from, $date_to)
{
$this->db->select('order_id, SUM(qty) as total, date');
$this->db->from("order");
$this->db->group_by('date');
$this->db->order_by('date','asc');
$query = $this->db->get();
log_message("debug", $this->db->last_query());
return $query->result();
}
how to use group_by date but only from the year, month and day?
If you want to group by date only like (y-m-d), not apply group by on time
then some change in your code like:
Some modification in your code
public function get_total_sales_per_day($date_from, $date_to)
{
$this->db->select('order_id, SUM(qty) as total, date');
$this->db->from("order");
$this->db->group_by(DATE_FORMAT('date', "%y-%y-%d"));
$this->db->order_by('date','asc');
$query = $this->db->get();
log_message("debug", $this->db->last_query());
return $query->result();
}
You just need to escape while using DATE() function
/*You have not used arguments (date_from, date_to)
anywhere in active record, decide whether you need that,
*/
public function get_total_sales_per_day($date_from, $date_to)
{
$this->db->select('order_id, SUM(qty) as total, date');
$this->db->from("order");
/* This is what you need, $escape = FALSE */
$this->db->group_by('DATE(`date`)',FALSE);
/* If field exists in select then
you can also use : $this->db->order_by('3','asc',FALSE);
meaning sort by 3rd field
OR like below more readable
*/
$this->db->order_by('DATE(`date`)','asc',FALSE);
/* if you wish to order by datetime then you may keep
$this->db->order_by('date','asc');
*/
$query = $this->db->get();
log_message("debug", $this->db->last_query());
return $query->result();
}
You may read source code here :
public function group_by($by, $escape = NULL)
public function order_by($orderby, $direction = '', $escape = NULL)

Symfony 4 Doctrine multiple table joins in one property

Very new to symfony and Doctrine. I have the following tables in my database.
mo_user
id | email | password
__________________________________
9144 | summer#h.com | !password!
mo_user_role
user_id| role_id
_________________
9144 | 5
mo_permission
id | namespace | name | description
______________________________________________
1 | admin | - | -
2 | users | - | -
3 | view_summary_report | - | -
4 | view_user_statement | - | -
mo_role_permission
role_id | permission_id
________________________
5 | 3
5 | 4
I am trying to return an array of the permissions of the current user in this case user with id = 9144 which should be array('view_summary_report','view_user_statement').
I have mapped all the tables to their corresponding entity classes. and in MoUser.php entity class which corresponds to mo_user table, I have a
permissions method which should return the array but my join from annotations is failing,
My getPermissions() method in MoUser.php
/**
* #var Collection|MoPermission[]
* #ORM\ManyToMany(targetEntity="App\Entity\MoPermission")
* #ORM\JoinTable(
* name="mo_user_role",
* joinColumns={#ORM\JoinColumn(name="user_id",referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="role_id",referencedColumnName="id")}
* )
*/
private $permissions;
public function getPermissions()
{
$currentPermissions = array();
foreach ($this->permissions->toArray() as $index => $permission) {
$currentPermissions[] = $permission->getNamespace();
}
//Return default role if Roles are not assigned to this user.
if(count($currentPermissions)>0) {
return $currentPermissions;
} else {
return array('DEFAULT_PERMISSION');
}
}
So I figured out the raw sql to achieve what I wanted which is below, but I would like to know the Symfony/Doctrine annotated way of achieving the following raw SQL.
SELECT t0.id AS id_1, t0.namespace AS namespace_2, t0.name AS name_3, t0.description AS description_4
FROM mo_permission t0
LEFT JOIN mo_role_permission ON t0.id = mo_role_permission.permission_id
LEFT JOIN mo_user_role ON mo_role_permission.role_id = mo_user_role.role_id
WHERE mo_user_role.user_id = 9144;
I don't think there is a proper way to achieve what you're trying to do directly through property annotations with your current setup.
You could achieve what you want with one of these solution though :
One of mo_user_role and mo_role_permission is not needed, since none of them have additional field. You should just have a mo_user_permission table generated by a ManyToMany relationship between MoUser and MoPermission, which would grant you direct access to MoPermission from MoUser's getPermissions()
Another way would be to create a service which would have a GetPermissionsFromUser(MoUser $moUser) method (for example), calling the proper query from the entity's repository, which you would call when needed.
You could still achieve what you want in your getPermissions() method with your current setup, but you would have to loop through each relation's items to build your new result array manually.
e.g. for last point :
public function getPermissions() {
$permissions = [];
foreach($this->roles as $role) {
foreach($role->getPermissions() as $permission) {
permissions[] = $permission->getNamespace();
}
}
return $permissions;
}
This would assume you have a MoRole entity, which would make sense regarding your current setup, but you didn't mention it. Otherwise, same logic could still be applied though, it's just a naming matter.
I'm pretty sure that you could do that query using Doctrine (and a QueryBuilder) like...
use Doctrine\ORM\EntityRepository
class PermissionRepository extends EntityRepository
{
//....
/**
* #param UserInterface $user
* #return array|Permission[]
*/
public function getPermissionsForUser(UserInterface $user)
{
$queryBuilder = $this->createQueryBuilder('permission');
/**
* permissions will be in a multi-dimensional array
* with a single key per array of 'namespace'
*/
$permissions = $queryBuilder
->select('permission.namespace')
->join('permission.role', 'role')
->join('role.user', 'user')
->where($queryBuilder->expr()->eq('user', ':user'))
->setParameter('user', $user)
->getQuery()
->getArrayResult();
if (count($permissions) > 0) {
/**
* If there are any permissions found just return
* the 'namespace' property from each "sub-array"
*/
return array_column($permissions, 'namespace');
}
return ['DEFAULT_PERMISSION'];
}
//...
}
And then you would call it like..
$permissions = $repository->getPermissionsForUser($user);

Filter Data Using Session Id

I want to filter data using session id. Have a table like below.
id | user_id | plan
1 | 1 |.sjhsjh.jpg
2 | 2 |bchdj.jpg
I want to filter that database value according to the login user.user id, which is the foreign key of user table. I have filter it before,but now its not working.
model
function get_plan(){
$this->db->select("floor_plan.id,floor_plan.image");
$this->db->where("user_id",$this->session->userdata['logged_in']['id']);
$this->db->from('floor_plan');
$query = $this->db->get();
return $query->result();
Session
<?php
if (isset($this->session->userdata['logged_in'])) {
$firstname = ($this->session->userdata['logged_in']['firstname']);
$email = ($this->session->userdata['logged_in']['email']);
$id = ($this->session->userdata['logged_in']['id']);
} else {
header("location: login");
}
change model as following.
function get_plan(){
$userdata=$this->session->userdata('logged_in');
$this->db->select("floor_plan.id,floor_plan.image");
$this->db->where("user_id",$userdata['id']);
$this->db->from('floor_plan');
$query = $this->db->get();
return $query->result();
}
session change as following.
<?php
if (isset($this->session->userdata('logged_in'))) {
$userdata=$this->session->userdata('logged_in');
$firstname = $userdata['firstname'];
$email = $userdata['email'];
$id = $userdata['id'];
} else {
redirect("login/index");
}

order and limit on a select query zend framework 2

Earlier this day a asked a question about an update query. But now i want to select some things ( and it is working ) but I also want to order them and put a limit on it.
This is the code to select all the food :
public function getFood($id)
{
$id = (int)$id;
$rowset = $this->tableGateway->select(array('kindOfFood_id' => $id));
$row = $rowset->current();
if (!$row) {
throw new \Exception("Could not find row $id");
}
return $row;
}
But how can i do this :
Select * from KindOfFood ==> order by kindOfFood_votes DESC ?
I saw on the documentation you can do something like this, but it doesn't work with me?
$rowset = $artistTable->select(function (Select $select) {
$select->where->like('name', 'Brit%');
$select->order('name ASC')->limit(2);
});
Are you looking to return only single row or multiple rows.
Try this for multiple rows -
use Zend\Db\Sql\Select; //at the top of the page among other use statements.
public function getFood($id)
{
$id = (int) $id;
$select = new Select(TABLE_NAME); //CHANGE TABLE_NAME as per needs
$select->where('kindOfFood_id = ' . $id);
$select->order('kindOfFood_votes DESC');
$resultSet = $this->tableGateway->selectWith($select); //Will get array of rows.
//$row = $rowset->current(); THIS IS FOR RETURNING ONLY SINGLE ROW NOT ALL ROWS
if (!$resultSet) {
throw new \Exception("Could not find rows with food id - $id");
}
return $resultSet;
}
Can access the returned resultSet via loop. Eg: foreach
foreach($resultSet as $row) {
echo $row->kindOfFood_id; //or something
}
Note:
If you need only
Select * from KindOfFood order by kindOfFood_votes DESC
then remove the $select->where('kindOfFood_id = ' . $id); line from above.