Returning array of objects in Laravel - laravel

I need to be able to change objects on server, save those, and return results back to frontend part of application.
So basically, I have some code that does manipulation of data, than Eloquent that does saving, and than I want to return Eloquent object back. Problem is that I have more than one object, that I'll manipulate, and right now, I'm putting all of them in array. When it comes back to my frontend all it has is this:
[{"incrementing":true,"timestamps":true,"exists":true}]
Here is the simplified code:
$results = array();
foreach ($tasks as $task){
//some manipulation
$result = Task::find($task['id']);
$result->order = $task['order'];
$result->save();
$results[] = $result;
}
return Response::json($results);

Ok, the solution was to call toArray() method before putting element to array.
$results[] = $result->toArray();

Related

Unserialize cart data not working Laravel

I'm save my cart products as unserialized data from my cart session.
$order = new Order();
$order->cart = serialize($cart);
$order->code = strtoupper(str_random(15));
$order->user_id = Auth::user()->id;
$order->save();
now i need to unserialize the data to use it in my blade file, this is the function i'm using
$orders = Order::with('user')->findOrFail($order->id);
$orders->transform(function($order, $key){
$order->cart = unserialize($order->cart);
return $order;
});
dd($orders);
I'm getting this error
BadMethodCallException Call to undefined method App\Order::transform()
what seems to be the problem? and how can i unserialize my data;
any ideas ???
With findOrFail you retrieve just one instance of model, not a collection.
$orders = Order::with('user')->findOrFail($order->id);
Also what is $order->id you have your order model?
So
$order->load('user');
$order->cart = unserialize($order->cart);
Or do you want a collection then your code will work like this
$orders = Order::with('user')->get();
$orders->transform(function($order, $key) {
$order->cart = unserialize($order->cart);
return $order;
});
The problem is not about unserialize, it doesn't even get there. The problem seems to be related to an undefined method you are trying to use ($orders->transform).
Can you please provide the code in your Order class? Did you define the transform method there?
EDIT:
by using serialize you are kinda' missing the point of a relational database and the datatypes inherent in your database engine. Doing this makes data in your database non-portable, difficult to read, and can complicate queries.
Also, many tests prove that json_encode is faster than serialize.

How I can paginate DB::select(...) result and get links() method?

I have a big and difficult SQL query. It works fine for me. And I have the following code in the controller:
public function index(OpenRegDepDataReportInterface $openRegDepDataReport, Request $request): Renderable
{
$applications = $openRegDepDataReport->getReport($advertisers, $category);
return view('applications.index', compact('applications'));
}
So, the method getReport gives me a result of DB::select('<here is my big diffecult SQL>'), and, as well known it's an array.
But as you can see I'm trying to pass the result on a view. And, of course, I would like to call $applications->links() in the view, like for eloquent collection. Which is proper and faster way to do that?
doc
To display pagination at the table, you must call the select and then the pagination method.
in Controller:
$test = DB::table('users')->select('id')->paginate(10);
in View:
$test->links();
So based on the documentation if your $applications returns a Query Builder result, then just append ->paginate(10); to it.
https://laravel.com/docs/master/pagination#paginating-query-builder-results
$applications = $openRegDepDataReport->getReport($advertisers, $category)->paginate(10);
Simple answer, use paginate() method:
$basicQuery = DB::select(DB::raw("<here is the big diffcult SQL query>"));
However, paginate() works only on collections, and since you have an array of objects, you need to turn it into a collection first, using the forPage() method:
The forPage method returns a new collection containing the items that would be present on a given page number. The method accepts the page number as its first argument and the number of items to show per page as its second argument:
$collection = collect($basicQuery);
$chunk = $collection->forPage(2, 3);
$chunk->all();
Complicated answer: build a paginator instance yourself:
$perPage = 10;
$page = $request->input("page", 1);
$skip = $page * $perPage;
if($take < 1) { $take = 1; }
if($skip < 0) { $skip = 0; }
$basicQuery = DB::select(DB::raw("<here is the big diffcult SQL query>"));
$totalCount = $basicQuery->count();
$results = $basicQuery
->take($perPage)
->skip($skip)
->get();
$paginator = new \Illuminate\Pagination\LengthAwarePaginator($results, $totalCount, $take, $page);
return $paginator;
I would recommend using the paginate() method.
You can read more about Laravel's pagination.

how to put and retrieve laravel collection object on Session

I know that we can insert array in Session as Session::push('person.name','torres') but how to keep laravel collection object such as $product = Product::all();,as like Session::put('product',$product);.Ho wto achieve this?
You can put any data inside the session, including objects. Seeing as a Collection is just an object, you can do the same.
For example:
$products = Product::all();
Session::put('products', $products);
dd(Session::get('products'));
Should echo out the collection.
You should convert it to a plain array, then convert them back to models:
Storing in the session
$products = Product::all()->map(function ($product) {
return $product->getAttributes();
})->all();
Session::put('products', $products);
Retrieving from the session
$products = Product::hydrate(Session::get('products'));
You can see an example of this method here.

filtering a paginated eloquent collection

I am trying to filter a paginated eloquent collection, but whenever I use any of the collection methods, I lose the pagination.
$models = User::orderBy('first_name','asc')->paginate(20);
$models = $models->each(function($model) use ($filters) {
if(!is_null($filters['type'])) {
if($model->type == $filters['type'])
return $model;
}
if(!is_null($filters['state_id'])) {
if($model->profile->state_id == $filters['state_id'])
return $model;
}
if(!is_null($filters['city_id'])) {
if($model->profile->city_id == $filters['city_id'])
return $model;
}
});
return $models;
I am working with Laravel 4.2, is there any way to persist the pagination?
An answer to the titular question, which is possible in Laravel 5.2+:
How to filter the underlying collection of a Paginator without losing the Paginator object?
You can eject, modify and inject the collection as follows:
$someFilter = 5;
$collection = $paginator->getCollection();
$filteredCollection = $collection->filter(function($model) use ($someFilter) {
return $model->id == $someFilter;
});
$paginator->setCollection($filteredCollection);
The Paginator is built on an underlying collection, but indeed when you use any of the inherited Collection methods they return the underlying collection and not the full Paginator object: collection methods return collections for chaining together collection calls.
Expanding on mininoz's answer with your specific case:
//Start with creating your object, which will be used to query the database
$queryUser = User::query();
//Add sorting
$queryUser->orderBy('first_name','asc');
//Add Conditions
if(!is_null($filters['type'])) {
$queryUser->where('type','=',$filters['type']);
}
if(!is_null($filters['state_id'])) {
$queryUser->whereHas('profile',function($q) use ($filters){
return $q->where('state_id','=',$filters['state_id']);
});
}
if(!is_null($filters['city_id'])) {
$queryUser->whereHas('profile',function($q) use ($filters){
return $q->where('city_id','=',$filters['city_id']);
});
}
//Fetch list of results
$result = $queryUser->paginate(20);
By applying the proper conditions to your SQL query, you are limiting the amount of information that comes back to your PHP script, and hence speeding up the process.
Source: http://laravel.com/docs/4.2/eloquent#querying-relations
I have a scenario that requires that I filter on the collection, I cannot rely on the Query Builder.
My solution was to instantiate my own Paginator instance:
$records = Model::where(...)->get()->filter(...);
$page = Paginator::resolveCurrentPage() ?: 1;
$perPage = 30;
$records = new LengthAwarePaginator(
$records->forPage($page, $perPage), $records->count(), $perPage, $page, ['path' => Paginator::resolveCurrentPath()]
);
return view('index', compact('records'));
Then in my blade template:
{{ $records->links() }}
paginate() is function of Builder. If you already have Collection object then it does not have the paginate() function thus you cannot have it back easily.
One way to resolve is to have different builder where you build query so you do not need to filter it later. Eloquent query builder is quite powerful, maybe you can pull it off.
Other option is to build your own custom paginator yourself.
You can do some query on your model before do paginate.
I would like to give you some idea. I will get all users by type, sort them and do paginate at the end. The code will look like this.
$users = User::where('type', $filters['type'])->orderBy('first_name','asc')->paginate(20);
source: http://laravel.com/docs/4.2/pagination#usage
This was suitable for me;
$users = User::where('type', $filters['type'])->orderBy('first_name','asc')->paginate(20);
if($users->count() < 1){
return redirec($users->url(1));
}
A workaround when mixing search parameters and pagination, since default pagination won't keep the search parameters, using GET:
$urlSinPaginado = url()->full();
$pos=strrpos(url()->full(), 'page=');
if ($pos) {
$urlSinPaginado = substr(url()->full(), 0, $pos-1);
}
[...]
->paginate(5)
->withPath($urlSinPaginado);
sample generated pagination link: http://myhost/context/list?filtro_1=5&filtro_2=&filtro_3=&filtro_4=&filtro_5=&filtro_6&page=8
You can simply use this format in views
{!! str_replace('/?', '?', $data->appends(Input::except('page'))->render()) !!}
For newer versions of Laravel, you can use this:
$data->paginate(15)->withQueryString();

laravel 4 paginate collection

I cant create a proper pagination system using laravel 4. I have the following models and function that return collections:
Model Restaurant:
public function fooditem()
{
return $this->hasMany('Fooditem','rest_id');
}
public function get_rest_foods($id){
return Restaurant::find($id)->fooditem->toArray();
}
The second function returns all food items for a certain restaurant as an array. I also use this in an API call.
in my controller i have this:
$food = $food->get_rest_foods($id);
$paginator = Paginator::make($food, 10, 5);
I pass the paginator to the view and it shows the links ok but also shows all my item from the food array.
I tried using
public function get_rest_foods($id){
return Restaurant::find($id)->fooditem->paginate(5);
}
but i get an error:
FatalErrorException: Error: Call to undefined method Illuminate\Database\Eloquent\Collection::paginate()
I searched this and many other sites but cant understant how to paginate a collection.
Thanks for your help
The paginator must get the items that it would normally get from a database query with an offset/limit statement.
So when you have a collection with all items, you should do the offset/limit yourself.
$food = $food->get_rest_foods($id);
$page = 1;
if( !empty(Input::get['page']) ) {
$page = Input::get['page'];
}
$perPage = 15;
$offset = (($page - 1) * $perPage);
$food = Paginator::make($food->slice($offset,$perPage, true)->all(), $food->count(), $perPage);
I created a subclass of Collection and implemented my own paginate method
public function paginate($perPage) {
$pagination = App::make('paginator');
$count = $this->count();
$page = $pagination->getCurrentPage($count);
$items = $this->slice(($page - 1) * $perPage, $perPage)->all();
$pagination = $pagination->make($items, $count, $perPage);
return $pagination;
}
The Laravel paginator does not do any of the splicing for you. Typically the paginator should be used hand in hand with Eloquent/Fluent. In your second example you should be doing it like this.
return Restaurant::find($id)->fooditem()->paginate(5);
Without calling the actual method you'll just be getting a collection of results and not the query builder instance.
If you want to use the paginator manually you need to pass in the spliced array (the correct items for the current page). This usually involves determining the current page, the total results and total pages. That's why, where possibly, it's best to use it with Eloquent or Fluent.

Resources