Laravel testing pagination with less data - php

I'm trying to lower my test times. Currently I have a test where I need to test the pagination links and meta data.
In my controller my pagination is set to 15 however in my test I have to create 16 instances using a factory to be able to assert the data on page 2.
TestGetStudents.php
public function testGetStudents() {
Students::saveMany(factory(Student::class, 16)->make());
$this->get('url/students/list?page=2')
->assertJson([
'meta' [
'current_page' => 2
]
]);
}
StudentController.php
public function list() {
return Students::paginate();
}
How do I write the test without having to create 16 students to test data on the 2nd page?

Replace your controller to receive a paginate parameter from user:
public function list(Request $request) {
$per_page = !($request->input('per_page')) ? 15 : $request->input('per_page');
return Studentes::paginate($per_page);
}

Related

How to break long array into subarrays returned from functions in PHP?

I have this PHP function in Laravel (in my database seeder):
DB::table('table_name')->insert([
[first_record],
[second_record],
...
[nth_record]
]);
I can't use faker libraries and I need to specify each record. However, I decided to break this method into smaller methods this way:
public function run()
{
DB::table('table_name')->insert([
$this->firstMethodToReturnPartOfArray(),
$this->secondMethodToReturnPartOfArray(),
...
$this->nthMethodToReturnPartOfArray()
]);
}
public function firstMethodToReturnPartOfArray()
{
return [
[first_record],
[second_record],
...
[nth_record]
];
}
public function secondMethodToReturnPartOfArray()
{
return [
[first_record],
[second_record],
...
[nth_record]
];
}
But I get this error:
Array to string conversion
at vendor/laravel/framework/src/Illuminate/Support/Str.php:524
I'm new to PHP and Laravel. How should I fix this?
The error is that for insert, where you have [first_record], - you are passing in an array of records (the return values of firstMethodToReturnPartOfArray etc is an array of records).
You could merge the results of the two (or more) methods and then use ... to put the results back into the call
DB::table('table_name')->insert([
...array_merge($this->firstMethodToReturnPartOfArray(),
$this->secondMethodToReturnPartOfArray(),
$this->nthMethodToReturnPartOfArray());
]);
(Note that the ... is intentional and not some form of abbreviation).
Or as I mentioned, load it from a file.

How to assert paginations in Laravel?

I have a category model with the following method:
public static function index()
{
return self::has('posts')->paginate(1);
}
My category controller:
public function index()
{
$categories = Category::index();
return view('categories.index', compact('categories'));
}
This is what I've tried, I am using RefreshDatabase trait.
public function test_index_view_is_working()
{
factory(Post::class, 5)->create();
$response = $this->get(route('categories.index'));
$response->assertViewHas('categories', Category::index());
}
This test fails for some reason:
Failed asserting that two objects are equal.
at tests/Feature/CategoryTest.php:38
37| $response->assertViewIs('categories.index');
> 38| $response->assertViewHas('categories', Category::index());
--- Expected
+++ Actual
## ##
'dispatchesEvents' => Array ()
'observables' => Array ()
'relations' => Array (
+ 'posts' => Illuminate\Database\Eloquent\Collection Object (...)
)
'touches' => Array ()
'timestamps' => true
The reason you get this error is because somehow the posts are eager loaded from the view/controller but not from the tests.
I'm guessing return self::has('posts')->with('posts')->paginate(1); could fix it.
Alternatively, you can test if you have the pagination at the bottom the page. Since {{ $categories->links() }} will add something like Previous and Next you can still look for it.
$response = $this->get(route('categories.index'));
$response->assertSee('Next');
Also, you can ensure that you paginate the categories but it won't ensure you have added the links at the bottom of the page.
use Illuminate\Contracts\Pagination\Paginator;
...
$response = $this->get(route('categories.index'));
$this->assertInstanceOf(Paginator::class, $response->viewData('categories'));
Are you running any migrations/factories in the setUp method of the test?
It looks like maybe there are no post records in your database so $categories is coming into the view as null.
Also side note if all you want to do is make sure that the view has the variable $categories you can use $response->assertViewHas('categories');. This is not ideal if you are wanting to make sure your view is getting actual data.

How can I add function name as route in Laravel 5.7?

I have a controller which returns enums for respective fields. e.g.
// Expected route - /api/getFamilyTypes - only GET method is allowed
public function getFamilyTypes()
{
return [
'Nuclear Family',
'Joint Family'
];
}
I've around 20 functions like this. How can I add this without manually adding an entry per function in routes file?
Thanks in advance.
In your routes file, add something like this,
Route::get('/something/{func}', 'SomeController#functionRoute');
Where something is whatever path you're wanting to use and SomeController is the controller with the 20 functions you're using and functionRoute is the action that we're about to make.
Then in your controller, make a function like this,
public function functionRoute($func)
{
return $this->$func();
}
This will make it so that whenever someone browses to /something/* on your website, it'll execute the function name at the end. So if you navigate to /something/getFamilyTypes it'll run your getFamilyTypes function.
This isn't particularly secure. If you do this, the user will be able to run any of the controller's methods. You could set up a blacklist like this.
public function functionRoute($func)
{
$blacklist = [
'secret',
'stuff',
];
return in_array($func, $blacklist) ? redirect('/') : $this->$func();
}
Or you could set up a whitelist like this,
public function functionRoute($func)
{
$whitelist = [
'getFamilyTypes',
'otherUserFriendlyStuff',
];
return in_array($func, $whitelist) ? $this->$func() : redirect('/');
}
If the responses are always from hard-coded arrays (as opposed to being from a database) then one way might be to have a variable in your route:
Route::get('/api/enum/{field}', 'EnumController#getField');
And then in your controller method, use the variable to get the correct data from a keyed array:
public function getField($field)
{
$fields = [
'family' => [
'Nuclear Family',
'Joint Family'
],
// ...
];
return $fields[$field];
}
If you want to continue using different methods for every field then Michael's answer is the easiest option, with one caveat. Allowing users to call any method by name on your controller is a security risk. To protect yourself, you should validate the method name against a whitelist.

passing variable from controller to the view in api rest codeigniter

I have two controllers, in the first which extends CI_Controller where I load my views ( represents my home page), in the second which extends REST_controller I have a class where I load my models and functions to get my arrays from sql requests to send them to my views.
So I'm not that I understood well how rest works but I want to know if my architecture is good like I explained.
I want also to know how I can display simple variables using rest, I mean if for example I have a function in my model such :
public function nb_bananas()
{
$query = $this->db->query('SELECT count(*) as nb
FROM bananas
');
if ($query->num_rows() > 0)
{
return $query->result_array();
}else{
return NULL;
}
}
before using rest I used to get nb of bananas in controller such as :
$data['nb_bananas]=$this->MyModel->nb_bananas();
and I display the number of bananas in my view such as :
<?php echo $nb_bananas[0]['nb'] ?>
My question is, now when I'm using rest I should get my return sql request in the controler such as :
public function nb_bananas_get(){
$nb_bananas=$this->MyModel->nb_bananas();
if ($nb_bananas){
$this->response($nb_bananas,REST_Controller::HTTP_OK);
}
else{$this->response([
'status' => FALSE,
'message' => 'No users were found'
], REST_Controller::HTTP_NOT_FOUND);}
Is that correct ?
And what is the simple way display just the nb_bananas in my view without using any buttun like in a Dashboard?
Or maybe even if in restful I can use the old method to pass my variable to the views ?
Thank you for your answers.

Showing custom SQL results

What do I want to do
I want to list data that is pulled from the database with a certain condition.
What do I have and what does it do
I have a function that calls the data. When I print_r the data, it throws the correct stuff, so the query is executing directly. However, the display isn't working. It shows all the data in the database.
Here is my function:
public function myfunction() {
$adminExtensions = $this->AdminExtension->find('all',
array(
'conditions' => array('location_id'=>'3')
)
);
//print_r($adminExtensions);
$this->set('adminExtensions', $this->paginate());
}
What is the problem
The problem, as stated, is that it doesn't list just the records with location_id == 3. It lists everything.
I have narrowed it down to the last line of the function, but I can't seem to get the right code in there.
My display file (myfunction.ctp) is a basic baked cakePHP index file.
What am I doing wrong?
The code you currently have calls two different find operations. $this->AdminExtension->find() will return an array with all the AdminExtensions with a location_id of 3. The second $this->paginate() call just returns all possible results suitable for pagination in the view.
If you want to filter the paginated results you have to either configure the $paginate variable in the Controller or do it directly before you call $this->paginate.
class PostsController extends AppController {
public $paginate = array(
'conditions' => array('location_id'=>'3')
);
}
This will adjust pagination for all $this->paginate calls in the controller.
To do it for only one paginate call:
public function your_view() {
$this->set('adminExtensions', $this->paginate('AdminExtension', array('location_id' => '3')));
);

Categories