I want to make an advanced search in which the user has optional parameters to search,am joining data from three tables as follows
$qry="SELECT rooms.*, salereservation.*, customers.*
FROM rooms
JOIN salereservation
ON salereservation.room_id = rooms.room_id
JOIN customers
ON customers.id = salereservation.customer_id
WHERE salereservation.sale_status=1 AND ";
i am appending to the query string the optional parameters as follows
if($fname!=''){
$qry.="fname LIKE %fname% AND ";
}
if($lname!=''){
$qry.="lname LIKE %:lname% AND ";
}
if($time_in!=''){
$qry.="start_datetime LIKE %time_in% AND ";
}
if($time_out!=''){
$qry.="end_datetime LIKE %time_out% AND ";
}
if($phone!=''){
$qry.="phone LIKE %phone% AND ";
}
if($room_no!=''){
$qry.="room_no LIKE %room_no%";
}
my problem is how to turn the code into laravel query builder
I didn't try this code but its should work under laravel 4.2.
$query = DB::table('rooms')
->join("salereservation", "salereservation.room_id", "=", "rooms.room_id")
->join("customers", "customers.id", "=", "salereservation.customer_id")
->where("salereservation.sale_status",'=',1)
->select('rooms.*', 'salereservation.*', 'customers.*');
if($fname!=''){
$query->where("fname",'like',"%$fname%");
}
if($lname!=''){
$query->where("lname",'like',"%$lname%");
}
if($time_in!=''){
$query->where("start_datetime",'like',"%$time_in%");
}
if($time_out!=''){
$query->where("end_datetime",'like',"%$time_out%");
}
if($phone!=''){
$query->where("phone",'like',"%$phone%");
}
if($room_no!=''){
$query->where("room_no",'like',"%$room_no%");
}
$data = $query->get(); //finally get the result
Update:
For query verification you can print your query using:
$queries = DB::getQueryLog();
$last_query = end($queries);
dd($last_query);
And verify if your query different from your desired query.
If something went to different we can upgrade our query structure according to them.and also can you update with your latest query generated from laravel methods.
But if you still face some difficulties to understand my point of view. let me know.
You have to setup your DB like this:
rooms:
- id
- room_no
reservations:
- customer_id
- start_datetime
- end_datatime
- room_id
customers:
- lname
- fname
- phone
Then setup the propers relationships in your models
//Reservation model
class Reservation extends \Eloquent {
public function room()
{
return $this->belongsTo('Room', 'room_id');
}
public function customer()
{
return $this->belongsTo('Customer', 'customer_id');
}
}
Create a query scope like the following :
// Reservation model
// $terms is an array which pairs column_name to the querying value
public function scopeSearch($query, $terms)
{
if ( ! empty($terms['room_no']))
{
$query->has('rooms.room_no', $terms['room_no']);
}
$customer_cols = ['lname', 'fname', 'phone'];
$reservations_cols = ['start_datetime', 'end_datetime'];
foreach ($terms as $key => $value)
{
if (in_array($key, $customer_cols))
{
$query->whereHas('customer', function($subquery) use ($key, $value)
{
$subquery->where($key, 'like', '%'.$value.'%');
});
}
if (in_array($key, $reservation_cols))
{
$query->where($key, $value);
}
}
}
Now you can invoke your query scope by doing the following:
$reservations = Reservation:search(array('fname' => 'value', 'lname' => 'value2', ...));
And accessing results by:
foreach ($reservations as $res)
{
$res->room->number;
$res->customer->name;
}
Related
I want to update multiple Departments against one unit. I tried this method, but it's not correct.
How can I update multiple departments ids?
Form:
Request:
Controller Function:
$pre_data = UnitDepartment::where('unit_id', $request->id)->get();
if ($pre_data) {
foreach ($pre_data as $value) {
$value->delete();
}
$department = $request->department_id;
foreach ($department as $value) {
$unitDepart = new UnitDepartment();
$unitDepart->unit_id = $request->id;
$unitDepart->department_id = $value;
$unitDepart->save();
}
}
table:
I found that is the table related to departments and units.
So you can build the relationship many-to-many between them,
Create the relationship in your models,
In Unit model:
public function departments()
{
return $this->belongsToMany('App\Unit','unit_department','unit_id','department_id');
}
In Department Model:
public function units()
{
return $this->belongsToMany('App\Department','unit_department','department_id','unit_id');
}
Attach the new relationship, simply use:
Unit::find($request->unit_id)->departments()
->sync($request->department_id);
Unfortunately, you cannot use softDelete on sync().
And I don't think you need to soft delete with unit_departments. As a pivot then it should be irrelevant if it is deleted or not.
And if user update the relationship on the frequent, this table will grow fast.
If you really need to soft-delete, you can write it like this:
$department_ids = $request->department_id;
$unit_id = $request->unit_id
// soft delete the unit_departments not in request:
UnitDepartment::where('unit_id', $unit_id)->whereNotIn('department_id', $department_ids)->delete();
// insert the new department_id+unit_id relationship
$exist_department_ids = UnitDepartment::where('unit_id', $unit_id)->whereIn('department_id', $department_ids)->pluck('department_ids')->all();
$dept_ids = array_diff($exist_department_ids, $department_ids);
$depts = collect($dept_ids)->map(function($dept_id) use ($unit_id) {
return ['department_id' => $dept_id, 'unit_id' => $unit_id];
});
UnitDepartment::insert($depts);
the problem is you're sending unit_id in the request, however using $request->id in the query which is wrong.
Change every occurance of $request->id with $request->unit_id in the controller.
to select pre data correctly
use
$pre_data = UnitDepartment::where('unit_id', $request->id)->first();
i tried this
$unit = UnitDepartment::where('unit_id', $request->unit_id)->get();
foreach ($unit as $item) {
$existDepartment[] = $item->department_id;
}
$newDepartment = $request->department_id;
$result = array_diff($newDepartment, $existDepartment);
if ($result) {
foreach ($result as $item) {
$data = new UnitDepartment();
$data->unit_id = $request->unit_id;
$data->department_id = $item;
$data->save();
}
}
I am making a messaging app, and i need to check if a conversation already exists that has a certain list of users (no more, no less). I have this model:
class Conversation{
public function users(){
return $this->belongsToMany('App\User');
}
public function messages(){
return $this->hasMany('App\Message');
}
}
I have these tables:
conversations:
id
user_id <- the owner of the conversation
users:
id
email
password
conversation_user:
id
conversation_id
user_id
I want to make a post request like this:
{
"users": [1,4,6], <- user ids
"message": "Some message"
}
If a conversation already exists with all and only users 1,4,6, the message should be added to that conversation to avoid having duplicate conversations in the database. Otherwise i will make a new conversation with the specified users.
This is the best i have been able to do so far:
$existing_conversation = $user->conversations()->whereHas('users',
function($query) use ($data){
$query->whereIn('user_id', $data['users']);
}
)->has('users', '=', count($data['users']));
But it just returns the conversations that has exactly the amount of users that was in the users array. It ignores the inner query..
Does anyone have an idea for this? :)
You can try the following query
$existing_conversation = $user->conversations()->wherePivotIn('user_id', $data['users'])->has('users', count($data['users'])->get();
Haven't tested, should work I think.
UPDATE
Not a very elegant solution, however it works. You can add some helper methods in your controller like
//Get user's conversations with no of users equal to count($data['users']);
protected function get_conversations_with_equal_users(User $user, array $user_ids)
{
return $user->conversations()
->wherePivotIn('user_id', $user_ids)
->has('users', '=', count($user_ids))
->get();
}
//Get the id of a user conversation with exactly same users as $data['users'] if it exists otherwise it will return 0;
protected function get_existing_conversation_id(User $user, array $user_ids)
{
$existing_conversation_id = 0;
$user_conversations_with_equal_users = $this->get_conversations_with_equal_users($user, $user_ids);
foreach($user_conversations_with_equal_users as $conv)
{
$ids = [];
foreach($conv->users as $user)
{
$ids[] = $user->id;
}
if($this->array_equal($user_ids, $ids))
{
$existing_conversation_id = $conv->id;
}
}
return $existing_conversation_id;
}
//Function to compare two arrays for equality.
protected function array_equal($a, $b) {
return (
is_array($a) && is_array($b) &&
count($a) == count($b) &&
array_diff($a, $b) === array_diff($b, $a)
);
}
The you can use the following in your controller to get the existing conversation for user (if it exists)
$existing_conversation_id = $this->get_existing_conversation_id($user, $data['users']);
if($existing_conversation_id)
{
$existing_conversation = Conversation::with('users')
->whereId($existing_conversation_id)
->get();
}
I need to call a function from view to echo a value. I use following code,
Controller (test_controller)
public function displayCategory()
{
$this->load->model('Model_test');
$data['categories'] = $this->Model_test->getCategories();
$this->load->view('test_view', $data);
}
public function display($id)
{
$this->load->model('Model_test');
$name= $this->Model_test->getName($id);
return $name;
}
Model (Model_test)
function getCategories() {
$query = $this->db->query("SELECT * FROM category");
if ($query->num_rows() > 0) {
return $query->result();
} else {
return NULL;
}
}
function getName($userId) {
$query = $this->db->query("SELECT name FROM user where id = '$userId' ");
if ($query->num_rows() > 0) {
return $query->row()->name;
} else {
return NULL;
}
}
View
<div id="body">
<?php
foreach ($categories as $object) {
$temp = $this->test_controller->display($object->id);
echo $object->title . " ". $object->no . $temp . '<br/>';
}
?>
</div>
but some error when running the code.
error Message: Undefined property: CI_Loader::$test_controller in view
I am not sure if you use CodeIgniter 2 or 3.
Anyway, basically you don't want to use anything inside View files except perhaps helper function(s) or some kind of "presenter" layer (that should be called inside controller I guess).
Solution using Join
Go and read this manual page and search for join. There you can learn about implementation of SQL join directive.
You want to modify this (getCategories()) function so it returns data that you require
function getCategories() {
$this->db->select('category.title, category.no, user.name as username')
->from('category')
->join('user', 'user.id = category.id');
$query = $this->db->get();
if ($query->num_rows() > 0) {
return $query->result();
} else {
return NULL;
}
}
and in view you can get your username like this
foreach ($categories as $object) {
echo $object->title . " ". $object->no . $object->username . '<br/>';
}
I am not 100% sure so please post comments I will edit this answer later.
Solution "breaking rules"
https://stackoverflow.com/a/24320884/1564365
general notes
Also consider naming your tables using plural so categories, users...
Also it is a bad practise to use "category.id as user.id" (storing user id inside category table in "id" field) instead you shold use either a pivot table or in case of 1:1 relation field "user_id".
I'm builind a form with laravel to search users, this form has multiple fields like
Age (which is mandatory)
Hobbies (optional)
What the user likes (optional)
And some others to come
For the age, the user can select in the list (18+, 18-23,23-30, 30+ etc...) and my problem is that i would like to know how i can do to combine these fields into one single query that i return to the view.
For now, i have something like this :
if(Input::get('like')){
$users = User::where('gender', $user->interested_by)->has('interestedBy', Input::get('like'))->get();
if(strlen(Input::get('age')) == 3){
$input = substr(Input::get('age'),0, -1);
if(Input::get('age') == '18+' || Input::get('age') == '30+' )
{
foreach ($users as $user)
{
if($user->age($user->id) >= $input){
$result[] = $user;
// On enregistre les users étant supérieur au if plus haut
}
else
$result = [];
}
return view('search.result', ['users' => $result]);
}
elseif (strlen(Input::get('age')) == 5) {
$min = substr(Input::get('age'), 0, -3);
$max = substr(Input::get('age'), -2);
$result = array();
foreach($users as $user)
{
if($user->age($user->id) >= $min && $user->age($user->id) <= $max)
$result[] = $user;
}
return view('search.result', ['users' => $result]);
}
}
else
$users = User::all();
And so the problem is that there is gonna be 2 or 3 more optional fields coming and i would like to query for each input if empty but i don't know how to do it, i kept the age at the end because it's mandatory but i don't know if it's the good thing to do.
Actually this code works for now, but if i had an other field i don't know how i can do to query for each input, i know that i have to remove the get in my where and do it at the end but i wanna add the get for the last query..
Edit: my models :
User.php
public function interestedBy()
{
return $this->belongsToMany('App\InterestedBy');
}
And the same in InterestedBy.php
class InterestedBy extends Model{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'interested_by';
public function users()
{
return $this->belongsToMany('App\User');
}
}
you can use query builer to do this as follow
$userBuilder = User::where(DB::raw('1')); //this will return builder object to continue with the optional things
// if User model object injected using ioc container $user->newQuery() will return blank builder object
$hobbies = Request::input('hobbies') // for laravel 5
if( !empty($hobbies) )
{
$userBuilder = $userBuilder->whereIn('hobbies',$hobbies) //$hobbies is array
}
//other fields so on
$users = $userBuilder->get();
//filter by age
$age = Request::input('age');
$finalRows = $users->filter(function($q) use($age){
return $q->age >= $age; //$q will be object of User
});
//$finalRows will hold the final collection which will have only ages test passed in the filter
A way you could possible do this is using query scopes (more about that here) and then check if the optional fields have inputs.
Here is an example
Inside your User Model
//Just a few simple examples to get the hang of it.
public function scopeSearchAge($query, $age)
{
return $query->where('age', '=', $age);
});
}
public function scopeSearchHobby($query, $hobby)
{
return $query->hobby()->where('hobby', '=', $hobby);
});
}
Inside your Controller
public function search()
{
$queryBuilder = User::query();
if (Input::has('age'))
{
$queryBuilder ->searchAge(Input::get('age'));
}
if (Input::has('hobby'))
{
$queryBuilder->searchHobby(Input::get('hobby'));
}
$users= $queryBuilder->get();
}
My model:
public function category($column, $value)
{
$this->db->select('c.cat2, c.category, m.id, m.date, m.when_date, m.when_time, m.where_m, m.age1, m.age2, m.opis, m.rozpoznamy');
$this->db->from('category c');
$this->db->where($column, $value);
$this->db->join('meeting m', 'm.id_cat = c.id');
$result = $this->db->get();
return $result->result();
}
public function delete($where, $column, $value)
{
$this->db->delete($this->users_m->category($column, $value), $where);
}
My controler:
public function delete()
{
$cat = $this->uri->segment(3);
$value = $this->uri->segment(4);
$column = 'm.id';
$where = array('m.id' => $value);
$this->users_m->delete($where, $column, $value);
redirect('main/category/' . $cat);
}
I have problem to delete data from join table, get this message when I try delete:
A Database Error Occurred
Error Number: 1146
Table 'ci.object' doesn't exist
DELETE FROM `Object` WHERE `m`.`id` = '13'
Filename: C:\xampp\htdocs\ci\system\database\DB_driver.php
Line Number: 330
So probably theres a problem with table in function delete. I try on different ways to get to this table and I don't know how to solve this. Any clue?
You're passing a CI Object into $this->db->delete() as $this->users_m->category() returns a DB result array of object(s).
CI Delete works by passing in the table name, in your case it looks like it will be
public function delete($where) {
$this->db->where('meeting m', $where);
$this->db->delete('meeting');
}
I can't work out why you have extra values in there