Retrieve all rows from table except few rows in laravel - mysql

I am using Laravel 5.1 & MySQL as backend to serve REST-API requests made from the mobile app.
I have a Cart table and Items table. So whenever user comes to 'Items screen' in his mobile App, In the backend I should perform 2 tasks.
First check if any Items are there in his cart. If yes (i.e.,there are items in Cart table), then fetch those items from Cart table and the remaining items from Item table.
If there are no items in Cart, then I will easily fetch all items from the Items table and show it to user.
I am stuck not being able to perform the task 1. Because I am first retrieving all the item_ids from the Cart table. It will return a collection. Now I should check if these item_ids(from cart table) are present in Items table. If yes, don't fetch those items from Items table, BUT fetch all other items from Items table. Combine those items from Items table & items from Cart table and show it to user. How can I achieve this?
Currently, problem is, I am getting all 'item_ids' from Cart table. It returns a collection of item-ids. Using foreach() loop, for every item_id, I am querying Items table as follows:
("*where('item_id','!=',$getItemId)->get();*")
$itemDetails = ItemBng::where('store_id','=',$store_id)->where('category_id','=',$category_id)->where('subcategory_id','=',$subcategory_id)->where('item_id','!=',$getItemId)->get();
And it returns collection checking against each individual item. and replaces collection with every new iteration in foreach loop.
Here is the function in my CartController:
public function getItemsWithCartItems($uuid,$store_id,$category_id,$subcategory_id,$subsubcategory_id=null)
{
try
{
$getCartItems = UserCartDetailBng::where('uuid','=',$uuid)->get();
if($getCartItems->isEmpty()) //Performing task 2. No problem here.
{
if($subsubcategory_id==null || $subsubcategory_id==0) // Bcoz, subsubcategory_id is optional
{
$itemDetails = ItemBng::where('store_id','=',$store_id)->where('category_id','=',$category_id)->where('subcategory_id','=',$subcategory_id)->get();
if($itemDetails->isEmpty()){
return ResponseService::buildFailureResponse("Store Item not found");
}
}
else
{
$itemDetails = ItemBng::where('store_id','=',$store_id)->where('category_id','=',$category_id)->where('subcategory_id','=',$subcategory_id)->where('subsubcategory_id','=',$subsubcategory_id)->get();
if($itemDetails->isEmpty()){
return ResponseService::buildFailureResponse("Store Item not found");
}
}
$count = $itemDetails->count();
$data = array('count'=>$count,'result'=>$itemDetails);
return ResponseService::buildSuccessResponse($data);
}
else //Performing task 1. Here is the problem.
{
// I am using "where('item_id','!=',$getItemId)->get()" And it returns collection checking against each individual item. and replaces collection with every new iteration in foreach loop.
foreach($getCartItems as $getCartItem)
{
$getItemId = $getCartItem->id;
if($subsubcategory_id==null || $subsubcategory_id==0)
{
$itemDetails = ItemBng::where('store_id','=',$store_id)->where('category_id','=',$category_id)->where('subcategory_id','=',$subcategory_id)->where('item_id','!=',$getItemId)->get();
if($itemDetails->isEmpty()){
return ResponseService::buildFailureResponse("Store items not found");
}
}
else
{
$itemDetails = ItemBng::where('store_id','=',$store_id)->where('category_id','=',$category_id)->where('subcategory_id','=',$subcategory_id)->where('subsubcategory_id','=',$subsubcategory_id)->where('item_id','!=',$getItemId)->get();
if($itemDetails->isEmpty()){
return ResponseService::buildFailureResponse("Store items not found");
}
}
$count = $itemDetails->count();
$data = array('count'=>$count,'result'=>$itemDetails);
return ResponseService::buildSuccessResponse($data);
}
}
}
please help me solve this problem, TIA.

Considering your models are set correctly, try this code
//get the cart items with the item
$cartItems = UserCartDetailBng::where('uuid','=',$uuid)->with('itemsBng')->get();
if($cartItems->isEmpty())
{
//
}
else
{
//find items excluding of cart items
$itemDetails = ItemBng::where('store_id','=',$store_id)
->where('category_id','=',$category_id)
->where('subcategory_id','=',$subcategory_id)
->whereNotIn('id', $cartItems->lists('item_id'))->get();
$data = array('cartItems'=>$cartItems,'generalItems'=>$itemDetails);
return ResponseService::buildSuccessResponse($data);
}

Consider adding the following to your code
Add this after try
$getCartItemsIds = UserCartDetailBng::where('uuid','=',$uuid)->lists('id');
Remove the foreach block and $getItemId from the else statement then have the $itemdetails retrieved using
$itemDetails = ItemBng::where('store_id','=',$store_id)->where('category_id','=',$category_id)->where('subcategory_id','=',$subcategory_id)->whereNotIn($getCartItemsIds)->get();
please note you have two different retrievals for $itemDetails edit the second one to also use the ->whereNotIn($getCartItemsIds)
Lastly as per your intention you should edit the $data returned to include also what is on the cart.
$data = array('count'=>$count,'result'=>$itemDetails, 'cart'=>$getCartItems);

Related

How to get last inserted id with insert method in laravel

In my laravel project I am inserting multiple records at time with modelname::insert method. Now I want to get last inserted id of it.I read somewhere when you insert multiple records with single insert method and try to get the last_record_id it will gives you the first id of the last inserted query bunch. But my first question is how to get last record id with following code .If I am able to get first id of the bunch .I ll make other ids for other record by my own using incremental variable.
Code to insert multiple record
if(!empty($req->contract_name) && count($req->contract_name)>0)
{
for($i=0; $i<count($req->contract_name); $i++)
{
$contract_arr[$i]['client_id'] = $this->id;
$contract_arr[$i]['contract_name'] = $req->contract_name[$i];
$contract_arr[$i]['contract_code'] = $req->contract_code[$i];
$contract_arr[$i]['contract_type'] = $req->contract_type[$i];
$contract_arr[$i]['contract_ext_period'] = $req->contract_ext_period[$i];
$contract_arr[$i]['contract_email'] = $req->contract_email[$i];
$contract_arr[$i]['created_at'] = \Carbon\Carbon::now();
$contract_arr[$i]['updated_at'] = \Carbon\Carbon::now();
$contract_arr[$i]['created_by'] = Auth::user()->id;
$contract_arr[$i]['updated_by'] = Auth::user()->id;
if($req->startdate[$i] != ''){
$contract_arr[$i]['startdate'] = date('Y-m-d',strtotime($req->startdate[$i]));
}
if($req->enddate[$i] != ''){
$contract_arr[$i]['enddate'] = date('Y-m-d',strtotime($req->enddate[$i]));
}
}
if(!empty($contract_arr)){
Contract::insert($contract_arr);
}
}
You should be able to call it like this
$lastId = Contract::insert($contract_arr)->lastInsertId();
If i see right, you're using a Model. Direct inserting only shows an success boolean. Try this instead:
Contract::create($contract_arr)->getKey()

Combining two items (recipe-like)

I'd like a user to be able to combine two items and if compatible will yield a new item. In this example, the item IDs will be saved as Strings.
I was wondering what the most efficient way to do this would be, while making sure that swapped order will always yield the same result, so the user could input the order:
item X + item Y = item Z
item Y + item X = item Z
I've tried using Dictionaries and Objects, but I just haven't been able to get anything to work. I've also tried some various libraries that include HashMap/HashSet but nothing is working. here's some pseduo-code:
itemRecipe1:HashSet = new HashSet();
itemRecipe1.add("2");//Add item with ID of 2
itemRecipe1.add("3");//Add item with ID of 3
inputRecipe:HashSet = new HashSet();
inputRecipe.add("3");//Add item with ID of 3 (swapped)
inputRecipe.add("2");//Add item with ID of 2 (swapped)
recipeList:HashMap = new HashMap();
receipeList.put(itemRecipe1, "11");//Recipe has been added, the result of the recipe should be item 11
//This should output as TRUE since the composition of itemRecipe1 and inputRecipe are the same, despite a different input order.
trace(receipeList.containsKey(inputRecipe));
If anyone has a solution for this issue, please elt me know as I am willing to implement any design I can get working. I just don't see how a Dictionary could work as the key order matters.
So you're trying to associate two or more objects with each other. The first thing you need is some primitive data you can use to represent each item uniquely, typically an ID. This should give you something like the following to begin with:
class Item {
public var _id:int;
public function Item(id:int) {
_id = id;
}
public function get id():int { return _id; }
}
Now you need some piece of data that establishes a relationship between multiple Items using this ID. That could be as simple as the following, with a little extra functionality thrown in to see if an input list of these IDs matches the relationship:
class ItemRelationship {
private var _items:Vector.<Item>;
public function ItemRelationship(items:Vector.<Item>) {
_items = items;
}
public function matches(ids:Vector.<int>):Boolean {
if (_items.length !== ids.length) {
return false;
}
for each (var item:Item in _items) {
var found:Boolean = false;
for each (var id:int in ids) {
if (item.id === id) {
found = true;
break;
}
}
if (!found) return false;
}
return true;
}
public function get items():Vector.<Item> { return _items; }
}
This lets us do something like this, assuming we have a bunch of items (item1, item2, ...) with IDs.
var rel:ItemRelationship = new ItemRelationship(new <Item>[item1, item2]);
And then:
trace(rel.matches(new <int>[1,2])); // true
trace(rel.matches(new <int>[2,1])); // true
trace(rel.matches(new <int>[3,4])); // false
Now all we need is something that stores all of these relationships and lets us fetch one based on a list of input IDs:
class RelationshipCollection {
private var _relationships:Vector.<ItemRelationship>;
public function RelationshipCollection(relationships:Vector.<ItemRelationship>) {
_relationships = relationships;
}
public function find(ids:Vector.<int>):ItemRelationship {
for each(var relationship:ItemRelationship in _relationships) {
if (relationship.matches(ids)) return relationship;
}
return null;
}
}
Put a load of relationships in there:
var collection:RelationshipCollection = new RelationshipCollection(new <ItemRelationship>[
new ItemRelationship(new <Item>[item1, item4]),
new ItemRelationship(new <Item>[item2, item3])
]);
And give it a whirl:
trace(collection.find(new <int>[1, 3])); // null (no match)
trace(collection.find(new <int>[1, 4])); // works
trace(collection.find(new <int>[3, 2])); // works
trace(collection.find(new <int>[2, 3])); // works
Of course for the sake of readability you can rename each class to something more appropriate for its application e.g. Item => Potion, ItemRelationship => Recipe, RelationshipCollection => RecipeBook.
so the user could input the order
The first step is to limit the possible input. If you allow any type of input, you have to parse that input and things get complicated very quickly.
Create an input method that only allows the user to put two items together, say for example via drag and drop of the items to only 2 slots.
I just don't see how a Dictionary could work as the key order matters.
The important part is to design the keys well.
As #George Profenza pointed out in the comments, you could change your IDs to a different format. Instead of having 1, 2, 3, ... n you could use 1, 2, 4, ... 2^n. The advantage is that you can combine any two IDs uniquely via bitwise or operator (|). In the following example, two such IDs are combined (binary notation):
00001
| 10000
--------
10001
As you can see, each ID occupies a separate position in binary: the 1st position and the 5th. Combining both via or operator means that now both 1st and 5th position are 1. The order doesn't matter. If you use such IDs in the form of powers of 2 you can combine them regardless of the order to form pairs, which can then be used as keys to a dictionary.
Another solution is to simply sort the pair of IDs.
The combination 3-2 becomes 2-3 and the combination 2-3 stays 2-3. Both 2-3 and 3-2 lead to the same result.
You can then build your data structure accordingly, that is: the outer data structure is for the lower ID number and the nested, inner one is for the bigger ID number. Here's some pseudo code with generic objects:
var map:Object = {};
map["2"] = {"3":"combination 2-3"};
To access that, you'd do something like:
trace(map[Math.min(ID1, ID2)][Math.max(ID1, ID2)])
There's also the brute force way of doing it by storing both possible combinations in the data structure. The code for that could roughly look like that:
var map:Object = {};
map["2"] = {"3":"combination 2-3"};
map["3"] = {"2":"combination 2-3"};
Now both
trace(map[ID1][ID2]);
and
trace(map[ID2][ID1]);
Should yield the same result.

Using ItemCollection on a BoxFolder type with Box API only returns 100 results and cannot retrieve the remaining ones

For a while now, I've been using the Box API to connect Acumatica ERP to Box and everything has been going fine until recently. Whenever I try to use a BoxCollection type with the property ItemCollection, I'll only get the first 100 results no matter the limit I set in the GetInformationAsync(). Here is the code snippet:
[PermissionSet(SecurityAction.Assert, Name = "FullTrust")]
public BoxCollection<BoxItem> GetFolderItems(string folderId, int limit = 500, int offset = 0)
{
var response = new BoxCollection<BoxItem>();
var fieldsToGet = new List<string>() { BoxItem.FieldName, BoxItem.FieldDescription, BoxItem.FieldParent, BoxItem.FieldEtag, BoxFolder.FieldItemCollection };
response = Task.Run(() => Client.FoldersManager.GetFolderItemsAsync(folderId, limit, offset)).Result;
return response;
}
I then pass that information on to a BoxFolder type variable, and then try to use the ItemCollection.Entries property, but this only returns 100 results at a time, with no visible way to extract the remaining 61 (in my case, the Count = 161, but Entries = 100 always)
Another code snippet of the used variable, I am basically trying to get the folder ID based on the name of the folder inside Box:
private static void SyncProcess(BoxFolder rootFolder, string folderName)
{
var boxFolder = rootFolder.ItemCollection.Entries.SingleOrDefault(ic => ic.Type == "folder" && ic.Name == folderName);
}
I wasn't able to find anything related to that limit = 100 in the documentation and it only started to give me problems recently.
I had to create a work around by using the following:
var boxCollection = client.GetFolderItems(rootFolder.Id);
var boxFolder = boxCollection.Entries.SingleOrDefault(ic => ic.Type == "folder" && ic.Name == folderName);
I was just wondering if there was a better way to get the complete collection using the property ItemCollection.Entries like I used to, instead of having to fetch them again.
Thanks!
Box pages folder items to keep response times short. The default page size is 100 items. You must iterate through the pages to get all of the items. Here's a code snippet that'll get 100 items at a time until all items in the folder are fetched. You can request up to 1000 items at a time.
var items = new List<BoxItem>();
BoxCollection<BoxItem> result;
do
{
result = await Client.FoldersManager.GetFolderItemsAsync(folderId, 100, items.Count());
items.AddRange(result.Entries);
} while (items.Count() < result.TotalCount);
John's answer can lead to a duplicate values in your items collection if there will be external/shared folders in your list. Those are being hidden when you are calling "GetFolderItemsAsync" with "asUser" header set.
There is a comment about it in the Box API's codeset itself (https://github.com/box/box-windows-sdk-v2/blob/main/Box.V2/Managers/BoxFoldersManager.cs)
Note: If there are hidden items in your previous response, your next offset should be = offset + limit, not the # of records you received back.
The total_count returned may not match the number of entries when using enterprise scope, because external folders are hidden the list of entries.
Taking this into account, it's better to not rely on comparing the number of items retrieved and the TotalCount property.
var items = new List<BoxItem>();
BoxCollection<BoxItem> result;
int limit = 100;
int offset = 0;
do
{
result = await Client.FoldersManager.GetFolderItemsAsync(folderId, limit, offset);
offset += limit;
items.AddRange(result.Entries);
} while (offset < result.TotalCount);

Check next record exists in database

I am using Zend fetch method to fetch huge number of records from database for creating reports.Since fetchAll is costly as compared to fetch i am using it.And if its the last row i need to add some additinoal logic.So my question is that is there a way to check if next record exists or not inside the while loop. I am using it like the following
//$select is the select query
$objDb = Zend_Registry::get('db');
$objAchQry = $objDb->query($select);
while($arrResult = $objAchQry->fetch()) {
//Do something
//I need to do something here if its the last record like
/*
if($last_rec)
do something
*/
}
Is there a way to check if the current one is last record or if any other record exists. I know to do it by taking count of records and incrementing a counter inside the loop.But i dont need it.Any solutions.?
"is there a way to check if next record exists" The condition on your while loop does exactly just that. The loop won't execute any more if no more rows exist to fetch.
Think of it this way:
while($arrResult = $objAchQry->fetch()) {
//Do something
}
// Now I'm just after the last record
/*
do something
*/
If you really need to do something before the last row is processed, you could modify your code to
$total_records = // get total no of rows
$counter = 0;
while($arrResult = $objAchQry->current()) {
//Do something
$counter ++;
//I need to do something here if its the last record like
if( ! $bojAchQry->next()) {
// The row currently being processed is the last one.
} else {
break;
}
}
One way - You can track with counter,
$total_records = // get total no of rows
$counter = 0;
while($arrResult = $objAchQry->fetch()) {
//Do something
$counter ++;
//I need to do something here if its the last record like
if($counter == $total_records ) // this iteration will be the last one.
//do something
}

Laravel - How to paginate here?

I have this code and I want to paginate $shares.
How can I archive this?
$level = Share::join('follows', 'shares.user_id', '=', 'follows.user_id')
->where('follows.follower_id', Auth::user()->id)
->where('follows.level', 1)
->get(array('shares.*'));
//get 10% of shares
$count = Share::count()/10;
$count = round($count);
$top10 = Share::orderBy('positive', 'DESC')
->take($count)
->get();
$shares = $top10->merge($level);
//get only unique from shares
$unique = array();
$uniqueShares = $shares->filter(function($item) use (&$unique) {
if (!in_array($item->id, $unique)) {
$unique[] = $item->id;
return true;
} else {
return false;
}
});
//order by id
$shares = $uniqueShares->sortBy(function($share)
{
return -($share->id);
});
return View::make('layout/main')
->with('shares', $shares);
lots of reudandant unnecessary codes here.
1st:
$level = Share::join('follows', 'shares.user_id', '=', 'follows.user_id')
->where('follows.follower_id', Auth::user()->id)
->where('follows.level', 1)
->get(array('shares.*'));
Why you are taking ALL the records only to discard it later?
2nd:
$shares = $top10->merge($level); Why you are merging the two arrays?
3rd:
$uniqueShares = $shares->filter(function($item) use (&$unique) {
if (!in_array($item->id, $unique)) {
$unique[] = $item->id;
return true;
} else {
return false;
}
});
You HAD to wrote this snippet because above in 2nd, you merged the two arrays which will yield duplicated entries. So why merging?
4th:
//order by id
$shares = $uniqueShares->sortBy(function($share)
{
return -($share->id);
});
And here comes the actual data which you actually want.
So let's recape
You need
10% of total shares
order by some positive column
order by amount of shares perhaps as i am guessing.
To use the inbuilt paginate(), you'l need paginate() that's a must.
Rest is simple.
count the total result. round(Share::count()/10)
put it in paginate() as the 1st arguement.
Add the order by clause whichever is necessary.
looking at the code, it doesn't look like you will/should have duplicated data which may haved added the distinct and group by clause.
use remember in Share::count()/10; to Cache it. You don't need to run the query over and over again.
and you're done.
The way you are merging your queries you may need to manually create it the pagination in your blade, then send a variable to "take" the next set you want.
Read the Laravel Docs for more info on implementing it into your views and manually creating it.
http://laravel.com/docs/pagination
Try this, should be good to go
Share::join('follows', 'shares.user_id', '=', 'follows.user_id')
->where('follows.follower_id', Auth::user()->id)
->where('follows.level', 1)
->paginate(20);
Maybe you would like to specify columns to select in select() method.