How to randomize results of ResourceCollection but still paginate api results - php

I have setup an API in Laravel that returns paginated results of Albums that are stored in my database. It currently returns 15 results with the rest paginated, however I want to randomize the results before paginating.
This is the code in my api.php
<?php
use Illuminate\Http\Request;
use App\Album;
Route::apiResource('/album', 'AlbumController');
This is the code for AlbumController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Album;
use App\Http\Resources\AlbumResource;
use App\Http\Resources\AlbumResourceCollection;
class AlbumController extends Controller
{
/**
* Return AlbumResource which wraps all data in an object named data.
*
* #param Album $album
* #return AlbumResource
*/
public function show(Album $album): AlbumResource {
return new AlbumResource($album);
}
public function index(): AlbumResourceCollection {
return new AlbumResourceCollection(Album::paginate());
}
}
I tried altering the index() function and I was able to randomize 15 results, however, I end up losing pagination. I did accomplished that with changing the function to this:
public function index(): AlbumResourceCollection {
return new AlbumResourceCollection(Album::all()->random(15));
}
What I want is something along the lines of this:
Get all results
Randomize results
Paginate it
I also attempted this but I got an error:
public function index(): AlbumResourceCollection {
return new AlbumResourceCollection(Album::all()->random()->paginate());
}
Any thoughts on what I can do to accomplish this?

The solution to my problem was as lagbox mentioned in the comments.
I updated my index function to the following:
public function index(): AlbumResourceCollection {
return new AlbumResourceCollection(Album::inRandomOrder(rand(0, 5000))->paginate());
}

Related

__construct(): Argument #1 ($config) must be of type array, string given

I've created a Laravel project on Laravel 9.20.0 and I'm attempting to use an API wrapper for Linnworks I found on Github (https://github.com/booni3/linnworks-laravel) within my project. I'm okay with raw PHP but admittedly a complete beginner when it comes to Laravel and object oriented PHP.
In my /routes/web.php file I have:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TestController;
Route::get('test', [TestController::class, 'test']);
In /app/Http/Controllers/TestController.php I have:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Services\OrderService;
class TestController extends Controller
{
protected $orderService;
/**
* Instantiate a new controller instance.
*
* #return void
*/
public function __construct(OrderService $orderService)
{
$this->orderService = $orderService;
}
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function test()
{
$this->orderService->calculateProfit();
}
}
In /app/Services/OrderService.php I have:
<?php
namespace App\Services;
use Linnworks;
class OrderService {
public function __construct()
{
}
public function calculateProfit()
{
$this->getOrder();
return;
}
public function getOrder()
{
$order = Linnworks::Orders()->GetOrdersByNumOrderId(777678);
echo "<pre>";
print_r($order);
echo "</pre>";
return;
}
}
At this point, I'm just trying to figure out the correct way to use the Linnworks API Wrapper from GitHub within my service class and get it to return something from the Linnworks platform.
I have input the correct API details in the .env file but I'm clearly doing something wrong or missing something obvious (likely due to my lack of experience with object oriented PHP and Laravel) and I'm getting the following error when I load my url /test/:
Booni3\Linnworks\Linnworks::__construct(): Argument #1 ($config) must be of type array, string given, called in /home/username/laravel-projects/project-name/vendor/booni3/linnworks-laravel/src/LinnworksServiceProvider.php on line 49
According to the GitHub readme, usage is as simple as:
$orders = Linnworks::Orders()->getOpenOrders(
25,
1,
null,
null,
'e41b4701-0885-430d-9623-d840d9d46dd6',
null);
Any help or pointers in the right direction will be hugely appreciated as I've hit a bit of a wall.
Thanks in advance

Class 'App/Person' not found in index.blade.php

I'm building a Laravel 5.6.27 application in PHP 7.2.5 where assignments can be created and given a task (task_id) and a person (person_id) to assign them to (along with dueDate, etc.). I'm getting this error when I try to load the page:
Class 'App/Person' not found (View: /Users/mmickelson/Sites/sacrament/resources/views/assignments/index.blade.php)
Here's the index.blade.php file:
#extends('layouts.master')
#section('title', 'Assignments')
#section('content')
<table>
#foreach($assignments as $assignment)
<tr>
<td>{{$assignment->person->firstName}}</td>
</tr>
#endforeach
</table>
#endsection
I'm showing only the firstName of the person to help make it as simple as possible and isolate the issue.
Here's the (beginning of) the AssignmentController.php:
use App\Assignment;
use App\Task;
use App\Person;
use Illuminate\Http\Request;
class AssignmentController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index() {
$assignments = Assignment::all();
return view('assignments.index', ['assignments'=>$assignments]);
}
...
Here's the (beginning of) the Assignment.php model:
namespace App;
use Carbon\Carbon;
use Collective\Html\Eloquent\FormAccessible;
use Illuminate\Database\Eloquent\Model;
class Assignment extends Model
{
use FormAccessible;
protected $fillable = ['person_id', 'task_id', 'dueDate', 'status', 'completedDate'];
public function person()
{
return $this->hasOne('App/Person');
}
...
The database table for "assignments" has a person_id column, as a foreign key to the "people" table.
If I remove person->name from index.blade.php, leaving the line as <td>{{$assignment}}</td>, the page loads fine but of course, shows the object. So, it seems to have something to do with accessing the Person associated with the Assignment.
Any ideas about what is causing the error?
I guess this issue because the name of class is wrong App/Person should be App\Person
and you can write it like the following:
public function person()
{
return $this->hasOne(\App\Person::class);
}
There is a minor mistake in your model:
public function person()
{
return $this->hasOne('App/Person');
}
Change the above code to below code:
public function person()
{
return $this->hasOne('App\Person');
}
it is better if you use the below code:
public function person()
{
return $this->hasOne(Person::class);
}
Person::class is same as App\Person.

Method Illuminate\Database\Query\Builder::filter does not exist

I am following a tutorial to write 2 classes for filtering threads in a forum application.I got this error in line
$threads = Thread::latest()->filter($filters); // in threadscontroller
Error:
Method Illuminate\Database\Query\Builder::filter does not exist.
ThreadsController with index method:
<?php
namespace App\Http\Controllers;
use App\Thread;
use App\Channel;
use App\Filters\ThreadFilters;
use Illuminate\Http\Request;
class ThreadsController extends Controller
{
public function __construct(){
$this->middleware('auth')->only('store','create');
}
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index(Channel $channel,ThreadFilters $filters)
{
$threads = Thread::latest()->filter($filters);
if($channel->exist){
$threads->where('channel_id',$channel->id);
}
$threads = $threads->get();
return view('threads.index',compact('threads'));
}
This is the abstract class Filters:
<?php
namespace App\Filters;
use Illuminate\Http\Request;
abstract class Filters{
protected $request;
protected $builder;
protected $filters = [];
public function __construct(Request $request){
$this->request = $request;
}
public function apply($builder){
$this->builder = $builder;
foreach($this->getFilters() as $filter=>$value){ //filter by,value yunus mesela.
if(method_exist($this,$filter)){
$this->$filter($value);
}
}
return $this->builder;
}
public function getFilters(){
return $this->request->intersect($this->filters);
}
}
Here ThreadFilters.php which extends filters class:
<?php
namespace App\Filters;
use App\User;
use Illuminate\Http\Request;
class ThreadFilters extends Filters
{
protected $filters =['by'];
protected function by($username){
$user = User::where('name',$username)->firstorFail();
return $this->builder->where('user_id',$user->id);
}
}
If I change latest to all, I get this error:
Type error: Argument 1 passed to
Illuminate\Support\Collection::filter() must be callable or null,
object given, called in
Also can anyone explain me what is $builder doing in those classes?
latest() is a modifier shortcut, equivalent to orderBy('created_at', 'desc'). All it does is add the ORDER BY constraint to the query.
filter() is a method on the Collection class. That method does not exist in the query builder, hence the "method not found" error you're receiving.
It does not appear that your filter class should be used with the resulting Collection. Rather, it adds conditionals to your original query. Try implementing it like this:
// Remove the filters() method here.
$threads = Thread::latest();
if ($channel->exist) {
$threads->where('channel_id', $channel->id);
}
// Pass your query builder instance to the Filters' apply() method.
$filters->apply($threads);
// Perform the query and fetch results.
$threads = $threads->get();
Also, for future questions, including the tutorial you're attempting/following can provide beneficial context to those helping you. :)
If you change latest to all, you're getting a Laravel Collection. So you are calling filter() on a Collection ($threads = Thread::all()->filter($filters);).
If you take a look into the code, you'll see, that the where() method of the array class gets called, which calls PHP's array_filter method. As you can see, a callable must be given.
But you are passing an Object to the filter method, $filters, which is an ThreadFilters-Object -> method injection here:
public function index(Channel $channel,ThreadFilters $filters) ...
Your error message answers your question in a great way:
Type error: Argument 1 passed to Illuminate\Support\Collection::filter() must be callable or null, object given, called in

How to change default Yii 2 Rest API GET response result

According to Yii 2 Rest APi documentation, I have a CountriesCountry that extends \yii\rest\ActiveController and a corresponding Countries model. This is the code for my Controller class.
<?php
namespace app\controllers;
class CountriesController extends \yii\rest\ActiveController{
public $modelClass = 'app\models\Countries';
public function actionIndex(){
}
public function actionView(){
}
public function actionCreate(){
}
public function actionUpdate(){
}
public function actionDelete(){
}
public function actionOptions(){
}
}
When I send a get request, it returns all the countries in my database.
My Question
is it possible to return my own result from action methods. Like in the actionIndex(), I will like to limit the result to 20 records. I did something like this but it is not working.
public function actionIndex(){
$model = Countries::find()->limit(20);
print_r($model);
}
I know that I can get all the countries from by database and loop through it and obtain only 20 results but I want to just query for 20 records from database.
Your class CountriesController that extends from \yii\rest\ActiveController automatically supports GET, PUT, POST calls etc. No need for actionIndex(), actionCreate(), etc if you just want regular REST functionality. Read about it in the Yii2 guide.
To limit the results you could just set another page size in your controllers afterAction-method. Add this to your controller. (I believe that 20 records is default of Pagination class, so if that is what you want you don't need this code at all. Just use the default functionality of yii/rest/ActiveController.)
public function afterAction($action, $result) {
if (isset($result->pagination) && ($result->pagination !== false)) {
$result->pagination->setPageSize(100);
}
return parent::afterAction($action, $result);
}
Suppose your api link is:
http://localhost/yii2-rest/api/country/?limit=15&order=id
Controller:
public function actionIndex(){
$model = Countries::find()
->orderBy($_GET['order'])
->limit($_GET['limit'])
->all();
return $model;
}
Take care about security!
You can get query string this way:
$limit = Yii::app()->getRequest()->getQuery('limit');
Straightway
//SELECT * FROM countries LIMIT 20
$countries= Countries::find()->limit(20)->all();

Laravel 5 and Angular Getting JSON From Controller

This is more of a concept question, so I apologize if it isn't specific enough.
I am coming from a Jquery/AngularJS background; usually I am doing front-end stuff and only work with a back-end sparingly.
I am trying to learn Laravel 5 to expand my skills, but am having trouble conceptually fitting together what I know from Angular with what Laravel is telling me.
I want CRUD functionality to a database using Angular, but I want Laravel to help me get that database from MySQL to JSON so it can be passed.
What I have done is made the following in Laravel:
~Model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Pun extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
public $timestamps = false;
}
~Controller:
namespace App\Http\Controllers;
use App\Pun;
use App\Http\Controllers\Controller;
class PunController extends Controller
{
/**
* Show a list of all available puns.
*
* #return Response
*/
public function index()
{
$puns = Pun::all();
return Response::json($puns->toArray());
}
}
}
~Route:
Route::get('showpunsfromdatabase', function () {
return view('???');
});
The question marks above is where I am having trouble. If I understand it correctly, the controller is supposed to query the database as defined in the model, and output the results as JSON. How do I then direct Angular to this controller so I can get this JSON using the $http service? Does it have to go in a view that then pulls it in? I don't really want it in a "view", I just want the data (JSON) available to Angular. Am I thinking about this correctly?
My angular controller looks like this:
$scope.punGenerate = function(){
$http.get("???").then(function (response) {$scope.thePunJSON = response.data;});
}
IF you do this:
Route::get('showpunsfromdatabase', function () {
return view('???');
});
You are not calling the controller, you are returning the view directly. To call the controller your route should look like this:
Route::get('showpunsfromdatabase' , [
'as' => 'showpunsfromdatabase.index',
'uses' => 'PunController#index'
]);
Now, from the controller you should load the view including the info returned by the model:
<?php
namespace App\Http\Controllers;
use App\Pun;
use App\Http\Controllers\Controller;
class PunController extends Controller
{
/**
* Show a list of all available puns.
*
* #return Response
*/
public function index()
{
$puns = Pun::all();
return view('???')
->with(['puns' => json_encode($puns)]);
}
}
It's just an example, because I don't know what are you looking for exactly.

Categories