I have a table named messages that has the structure as follow:
id | sender_id | receiver_id | message | sent_at
I have made a system that whenever a user selects a particular user the messages that are sent by or received by him are shown in the page in proper manner. For that I have the following code that does the thing:
<script type="text/javascript">
function showMessages(elem){
let id = $(elem).attr('id');
let myId = "<?php echo \Session::get('user_id'); ?>";
console.log(id, myId);
$.get("/get-messages",
{
id_no_1: id,
id_no_2: myId
}, function(data){
alert(data)
});
}
Here, I get the user_id of the associated user and my own user and perform an AJAX request to the route and the route has:
Route::get('/get-messages', 'PagesController#getMessages');
And my major function looks like this:
public function getMessages(){
$id_no_1 = request('id_no_1');
$id_no_2 = request('id_no_2');
$messagesSendByOneToTwo = DB::table('messages')
->where('sender_id', $id_no_1)
->where('receiver_id', $id_no_2)
->get();
// $messagesSendByOneToTwo = json_encode($messagesSendByOneToTwo);
$messagesSendByTwoToOne = DB::table('messages')
->where('sender_id', $id_no_2)
->where('receiver_id', $id_no_1)
->get();
// $messagesSendByOneToTwo = json_encode($messagesSendByTwoToOne);
$allMessages = array_merge($messagesSendByOneToTwo, $messagesSendByTwoToOne);
$allMessages = json_encode($allMessages);
echo $allMessages;
}
There I have two variables and I want to merge the two variable into a single variable. I tried array_merge(); which I knew wouldn't work because the variables aren't arrays. To this problem, I have the following possible solutions:
Merge the two variables into one and perform things with it. (But, I don't know how to do that.
Send two variables as the response to the JavaScript function handling the response (which I have never heard of).
Returning to a new view with the variables (which doesn't seem to be a good idea)
So, what should I do to solve this problem?
The best way would be to merge the variables into a new one so what should I do here?
There's no reason for you to do two queries to begin with:
public function getMessages(){
$id_no_1 = request('id_no_1');
$id_no_2 = request('id_no_2');
$messagesSent = DB::table('messages')
->whereIn('sender_id', [ $id_no_1, $id_no_2 ])
->whereIn('receiver_id', [ $id_no_1, $id_no_2 ])
->get();
return response()->json($messagesSent); //This is better than echoing json
}
If you want an array you can try following code to get array instead of collection.
messagesSendByOneToTwo = DB::table('messages')
->where('sender_id', $id_no_1)
->where('receiver_id', $id_no_2)
->get()->toArray();
Related
I am posting Laravel 5.2 real life routing use case and would like to have an answer for it. Problem: same url structure for multiple different database lookups. Please do not post remarks on how to make easier URL structure, this is the way the structure must be and many sites use it in this segment.
URL structure
domain.com/{slug1}/{slug2}/{slug3}
// e.g. domain.com/cottages/slovakia/cheap
// {slug1} - DB table accommodation_types (20+)
// {slug2} - DB table locations (300+)
// {slug3} - DB table accommodation_categories e.g. cheap etc. (100+)
domain.com/{slug1}/{slug2}
// e.g. domain.com/cottages/cheap OR domain.com/slovakia/cheap
// {slug1} - DB table accommodation_types OR locations
// {slug2} - DB table locations OR accommodation_categories
domain.com/{slug}
// DB table accommodation (10000+ entries)
// or
// accommodation_types OR locations OR accommodation_categories
How would you do it nicely? I have these ideas.
a. Use closure and call appropriate controller after examining url segments?
Route::get('{slug1}', function ($slug1, $slug2 = null, $slug3 = null)
{
// Check accommodation
$object = Accommodation::where('slug', $slug1)->first();
if( !is_null($object) )
{
return app()->make('AccommodationDetailController')->view($object);
}
// Check type
$type = AccommodationType::where('slug', $slug1)->first();
if( !is_null($type) )
{
return app()->make('AccommodationListController')->view($type);
}
// etc.
});
b. Generate thousands of urls by for loop and cache it then?
I appreciate any other great solution
I think the best way is to send all these routes to the same controller action and edit your query according to the parameters that are sent.
For example, this would be your routing file:
<?php
Route::get('{slug1}', 'Controller#getPage');
Route::get('{slug1}/{slug2}', 'Controller#getPage');
Route::get('{slug1}/{slug2}/{slug3}', 'Controller#getPage');
In the controller, you can use Eloquent or the query builder to build the sql query according to the variables you received from the route. Below is a simple example:
<?php
class Controler {
public function getPage($slug1, $slug2 = null, $slug3 = null) {
$models = Model::where(function ($query) use ($slug1) {
$query->where('accomodation_types', $slug1)
->orWhere('location', $slug1);
})
->where(function ($query) use ($slug2) {
if (isset($slug2)) {
// Slug2 query
}
})
->where(function ($query) use ($slug3) {
if (isset($slug3)) {
// Slug3 query
}
})
->get();
}
}
I’m new to Laravel. I’m using Laravel 5.2 and I’ve faced a problem in inserting data in to pivot table which I used to handle a many to many relationship. For passing data into the sever I’m using a jquery ajax post request. It’s code is as follows.
$("#btnSave").click(function(){
var path = JSON.stringify(route);
var token = $('input[name="_token"]').val();
$.post("/tour",
{
tourname: $("#name").val(),
startpoint: $("#select_startpoint").val(),
endpoint : $("#select_endpoint").val(),
waypoints : path,
'_token': token
},function(){
alert("Path has been saved");
window.location.href = "/tour";
}); });
Here route is a JavaScript array with set of strings and I’m using Json to pass the values in server. Here I’m using an RESTful resource controller to handle the request and its store method is as follows.
public function store(Request $request){
$user = Auth::user();
$tour = new Tour;
$tour->name = $request->tourname;
$tour->user_id = $user->id;
$tour->startpoint = $request->startpoint;
$tour->endpoint = $request->endpoint;
$tour->save();
$json = $request->waypoints;
$waypoints = json_decode($json);
foreach($waypoints as $waypoint){
$city = City::where('name', '=', $waypoint)->firstOrFail();
$tour->cities()->attach($city->id);
} }
Here in inserting data into pivot table I want obtain the city_id of a particular city from the database first, as I only have it’s name in the array.
As I execute the code the tour table get updated correctly but the pivot table (city_tour) does’nt. When I was further debugging I noticed when a integer value is custom assigned (As example: $tour->cities()->attach(2); ) the code works fine. It seems there is a problem in assigning the value to the $waypoint variable inside the query. But I can’t figure it out, help is much appreciated.
You could try where('name', 'LIKE', "%$waypoint%" )..... "=" usually doesn't play well with strings unless its an exact match.
LIKE in SQL gets closest match.
Use of % with LIKE:
searching for the City 'Algiers'.
this would find the city
$city = 'Algiers';
City::where('name', 'LIKE', "$city" )->firstOrFail();
if you have a white space then you might get nothing
$city = ' Algiers';
City::where('name', 'LIKE', "$city" )->firstOrFail();
if you use % then the space or character is ignored.
$city = ' Algiers'; //extra space on the end
City::where('name', 'LIKE', "%$city" )->firstOrFail();
or if you want to ignore any deviations from the end of the word:
$city = 'Algier'; //with 's' missing
City::where('name', 'LIKE', "$city%" )->firstOrFail();
or you dont have to use LIKE but you make sure the $city is in the column.
Hope that helps
Sorry if my title is confusing, not sure how to explain this within a line. Let's say I have a table with some columns and I have this
$model = Document::where('systemName', '=', $systemName)->where('ticketNumber', '=', ($nextTicketNumber))->get(); ticketNumber is unique where as there are quite a few systemNames
The above will get exactly what I want but I want more. I want an another array which will store all the rows under the same systemName. I know I can do this by doing
$allSystemNameModel = Document::where('systemName', '=', $systemName)
But is there a possible way to not having two variables and be easier?
No, you can't get both collections into one variable with one statement, however, you can create an array and store your results there:
$both = [];
$both['model'] = ...
$both['all'] = ...
UPDATE:
To avoid querying the database twice, you can use a first method that laravel provides us with.
$allSystemNameModel = Document::where('systemName', '=', $systemName);
$model = $allSystemNameModel->first(function ($doc) use ($nextTicketNumber) {
return $doc->ticketNumber == $nextTicketNumber;
});
$both['model'] = $model;
$both['all'] = $allSystemNameModel->all();
Note: Be sure to use use when working with php closures since $nextTicketNumber will be undefined otherwise.
I have a search query that needs to be done. However, a search doesn't always have all values set, like in this case.
$aEvents = DB::table('events')
->where('client_id', '=', $client_id);
The question is, how can I make this where statement depend on the value of $client_id. So if the value is empty I don't want the Where statement to occur.
Also, I do not want to write several complete queries with if statements in PHP. To many variables. Ideally I'd like something like this:
$aEvents = DB::table('events')
->(($client_id != "") ? where('client_id', '=', $client_id) : "");
Using eloquent is (really!) nice and save, but I'm not yet up to speed with if statements in std Class objects I guess. Any help is appreciated.
You may try something like this:
$query = DB::table('events');
if(!empty($client_id)) {
$query->where('client_id', $client_id);
}
$aEvents = $query->get(); // Call this at last to get the result
If you are passing client_id to the server via a form/query string(user input) then you may try something like this:
if($client_id = Input::get('client_id')) {
$query->where('client_id', $client_id);
}
Update: For pagination try this:
$aEvents = $query->paginate(10); // For 10 per page
So you may call links() method in your view if you pass it like this:
return View::make('viewName')->with('aEvents', $aEvents);
In the view for pagination links:
$aEvents->links()
You can also use query scopes in the model for this purpose. Scopes allow you to easily re-use query logic in your models. In the model Event, you can add the following query scope:
public function scopeClientID($query, $client_id)
{
if ($client_id != '') {
return $query->where('client_id', '=', $client_id);
} else {
return $query;
}
}
Then from your controller or wherever you're calling it from, you can do the following:
$aEvents = Event::clientID($client_id);
If you want to get all the results, then you can do:
$aEvents = Event::clientID($client_id)->get();
Or if you want pagination, you can do:
$aEvents = Event::clientID($client_id)->paginate();
You can also chain it with other methods like you'd do in a eloquent query.
You can read more about model query scopes at http://laravel.com/docs/eloquent#query-scopes
I am building an online tool for soccerclubs in php. Part of the system is a match module where you can make a video analysis, give grades to players, and more.
Everything is working fine, only problem being the performance of the initial pageload. The reason behind the problem is the database and the way im getting and presenting the data from the database.
My current database looks like this(I removed all unnecessary fields):
http://i.stack.imgur.com/VZnpC.png
When I'm done getting my data from my database I have an object like this:
$match->formations[]->positiongroups[]->positions[]->users[]->statgroups[]->stats[]
The way I'm getting the data takes way to much time( about 12 seconds ) and I'm probably doing it completely wrong. You can see the code below. I use laravel 4 as framework so most code isn't plain php but I think when you read the code you will understand what every line of code does. I do want to notice that an non-laravel solution is fine!
/*
* Get fullmatch info
* Returned value consists of
* $match->formation->positiongroups[]->positions[]->users[]->statgroups[]->stats[]
*/
public static function fullMatch($id){
//Get match of given id with formation and team
$match = Match::where('id', '=', $id)->with('formation', 'team.formation.positions', 'team.category')->first();
if($match->formation){
//Set all positiongroups in $match->formation
$match->formation->positiongroups = Positiongroup::all();
//Get possible positions
foreach(Formation::find($match->formation->id)->positions as $position){
$positions[] = $position->id;
}
//Loop through all positiongroups in $match->formation
foreach($match->formation->positiongroups as $positiongroup){
//Set all positions in positiongroups
$positiongroup->positions = Position::where('positiongroup_id', '=', $positiongroup->id)->whereIn('id', $positions)->get();
foreach($positiongroup->positions as $position){
$position->users = DB::table('formation_position_match_user')
->leftJoin('users', 'user_id', '=', 'users.id')
->where('formation_id', '=', $match->formation->id)
->where('position_id', '=', $position->id)
->where('match_id', '=', $match->id)
->get();
foreach($position->users as $user){
$user->statgroups = Statgroup::where('video', '=', 1)->with('Stats')->get();
$user->stats = DB::table('stat_statdate_user')
->leftJoin('statdates', 'statdate_id', '=', 'statdates.id')
->where('stat_statdate_user.user_id', '=', $user->id)
->groupBy('stat_statdate_user.stat_id')
->orderBy('stat_statdate_user.stat_id')
->get();
}
}
}
}
return $match;
}
If there is more information needed I'm happy to add it to the post.
I haven't seen the queries it produces, but I think you have too many nested foreach loops and you are sending too many queries to database. You should minimize number of queries. You can do it manually or use some library. For example NotORM
edit: Here is example of what you could easily do and improve the performance:
You should focus on getting more data at once, than doing it row by row. For example replace some = in WHERE conditions with IN ();
so instead of sending a lot of queries like
SELECT * FROM positions WHERE position_group_id = x;
SELECT * FROM positions WHERE position_group_id = y;
SELECT * FROM positions WHERE position_group_id = z;
you send only one SELECT to server:
SELECT * FROM positions WHERE position_group_id IN (x, y, z);
You will have to modify your code, but it won't be that difficult... I don't know how your where method works, and if it supports IN statement, but if it does, do something like this:
$position_group_ids = array();
foreach ($match->formation->positiongroups as $positiongroup) {
$position_group_ids[] = $positiongroup->id;
}
$all_positions = Position::where('positiongroup_id', 'IN', $position_group_ids);