Use crawler in controller - php

// src/Acme/DemoBundle/Tests/Controller/DemoControllerTest.php
namespace Acme\DemoBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class DemoControllerTest extends WebTestCase
{
public function testIndex()
{
$client = static::createClient();
$crawler = $client->request('GET', '/demo/hello/Fabien');
$this->assertGreaterThan(0, $crawler->filter('html:contains("Hello Fabien")')->count());
}
}
this working OK in my tests, but i would like use this crawler also in controller. How can i do it?
i make route, and add to controller:
<?php
// src/Ens/JobeetBundle/Controller/CategoryController
namespace Acme\DemoBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Acme\DemoBundle\Entity\Category;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class CategoryController extends Controller
{
public function testAction()
{
$client = WebTestCase::createClient();
$crawler = $client->request('GET', '/category/index');
}
}
but this return me error:
Fatal error: Class 'PHPUnit_Framework_TestCase' not found in /acme/vendor/symfony/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php on line 24

The WebTestCase class is a special class that is designed to be run within a test framework (PHPUnit) and you cannot use it in your controller.
But you can create a HTTPKernel client like this:
use Symfony\Component\HttpKernel\Client;
...
public function testAction()
{
$client = new Client($this->get('kernel'));
$crawler = $client->request('GET', '/category/index');
}
Note that you will only be able to use this client to browse your own symfony application. If you want to browse an external server you will need to use another client like goutte.
The crawler created here is the same crawler returned by WebTestCase so you can follow the same examples detailed in the symfony testing documentation
If you need more information, here is the documentation for the crawler component and here is the class reference

You shouldn't use WebTestCase in prod environment, because WebTestCase::createClient() creates test client.
In your controller you should do something like this (I recommend you to use Buzz\Browser):
use Symfony\Component\DomCrawler\Crawler;
use Buzz\Browser;
...
$browser = new Browser();
$crawler = new Crawler();
$response = $browser->get('/category/index');
$content = $response->getContent();
$crawler->addContent($content);

Related

Functional testing a controller called via twig render method

I have the following controller:
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class SidebarController extends Controller
{
public function displayCurrentUserInfoToTheSideBarAction()
{
$auth_checker = $this->get('security.authorization_checker');
$token = $this->get('security.token_storage')->getToken();
$user = $token->getUser();
return $this->render('widgets/sidebar_profile.html.twig',['user'=>$user]);
}
}
And I want somehow to test it but I am stuck because I invoke this method via twig's render function:
{{ render(controller('AppBundle:Sidebar:displayCurrentUserInfoToTheSideBar')) }}
What actually I want t ensure is that this particular controller renders the correct html. Usual functional tests on controllers in symphony is by emulating a homepage visit and checking any http related such as headers, response code and parsing the html in order to see if the site's behavior is as it should be, for example:
namespace Tests\AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class DefaultControllerTest extends WebTestCase
{
public function testIndex()
{
$client = static::createClient();
$crawler = $client->request('GET', '/');
$response=$client->getResponse();
$this->assertEquals(302, $response->getStatusCode());
$this->assertEquals('/login',$response->headers->get('Location'));
}
}
This test checks whether a visit to the homepage redirects to the login screen. But on the controller above how I can perform similar tests?

include class with autoload.php but getting class not found error

I tried to use jasonmapper just as written in manual.
I required autoload.php file, and when construct JasonMapper object, I go class not found exception.
(1/1) FatalThrowableError
Class 'App\Http\Controllers\JsonMapper' not found
Here is my code
namespace App\Http\Controllers;
require __dir__.'/../../../vendor/autoload.php';
use Illuminate\Http\Request;
use App\Http\Games\Numbers;
class ApiController extends Controller
{
public function home()
{
$client = new \GuzzleHttp\Client();
$res = $client->request(
'GET',
$testurl
);
$json = json_decode($res->getBody());
$mapper = new JsonMapper();// error occurs at this line
$numbers = $mapper->map($json, new Numbers());
return json_encode($numbers);
}
}
If you don't "use" JsonMapper at the top of your script, PHP assumes that JsonMapper is in the App\Http\Controllers namespace, which it's not. That means in your script you must:
$mapper = new \JsonMapper();

Fatal Error trying to use GuzzleHttp in Laravel 5.2

I'm trying to create a POST request using Laravel 5.2 and GuzzleHttp Client. I've successfully installed GuzzleHttp with Laravel but it just keeps repeating an error.
Fatal error: Call to undefined function App\Http\Controllers\API\Client()
Here is my code.
<?php
namespace App\Http\Controllers\API;
use Closure;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\API\APIConfiguration;
use App\Http\Controllers\Controller;
use GuzzleHttp\Client;
class APIController extends Controller {
public function __construct(Request $request){
$this->request = $request;
}
public function doShardDetails(Request $request) {
$APIConfig = new APIConfiguration();
$client = Client();
$json = $APIConfig->jsonTemplate("Method");
$request = $client->post("IP:PORT", $json);
return $request;
}
}
I've been trying to fix this for hours, nothing on the internet. :(
You have a typo:
$client = Client();
You should create a new object:
$client = new Client();
Fatal error: Call to undefined function App\Http\Controllers\API\Client()
you need import Client class right way - with it own namespace, because namespace App\Http\Controllers\API do not have class name Client

Extend Request class in Laravel 5

I'm new to Laravel (only experienced Laravel 5, so no legacy hang up here)
I'd like to know how to extend the core Request class. In addition to how to extend it, i'd like to know if it's a wise design decision to do so.
I've read through the documentation extensively (especially with regards to registering service providers and the manner in which it provides Facades access to entries within the dependency container) - but I can see (and find) no way to replace the \Illuminate\Http\Request instance with my own
Here is Official Document: Request Lifecycle
Content of app/Http/CustomRequest.php
<?php namespace App\Http;
use Illuminate\Http\Request as BaseRequest;
class CustomRequest extends BaseRequest {
// coding
}
add this line to public/index.php
$app->alias('request', 'App\Http\CustomRequest');
after
app = require_once __DIR__.'/../bootstrap/app.php';
change the code at public/index.php
Illuminate\Http\Request::capture()
to
App\Http\CustomRequest::capture()
I was working on the same issue today and I think it's worth mention that you may just change
Illuminate\Http\Request::capture()
to
App\Http\CustomRequest::capture()
without adding line
$app->alias('request', 'App\Http\CustomRequest');
because inside capture() method laravel actually binds provided class to service container with 'request' as a key
I guess you will have to extend also RequestForm. I use trait to avoid code duplication. Code below is relevant for Laravel 5.3.
app/Http/ExtendRequestTrait.php
<?php
namespace App\Http\ExtendRequestTrait;
trait ExtendRequestTrait {
methodFoo(){}
methodBar(){}
}
app/Http/Request.php
<?php
namespace App\Http;
use Illuminate\Http\Request as BaseRequest;
class Request extend BasicRequest {
use ExtendRequestTrait;
}
app/Http/FormRequest.php
<?php
namespace App\Http;
use Illuminate\Foundation\Http\FormRequest as BaseFormRequest;
class FormRequest extend BasicFormRequest {
use ExtendRequestTrait;
}
For phpunit test working you will have to override call method to make it using right Request class here Request::create.
test/TestCase.php
<?php
use App\Http\Request;
abstract class TestCase extends Illuminate\Foundation\Testing\TestCase{
// just copy Illuminate\Foundation\Testing\TestCase `call` method
// and set right Request class
public function call($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
{
$kernel = $this->app->make('Illuminate\Contracts\Http\Kernel');
$this->currentUri = $this->prepareUrlForRequest($uri);
$this->resetPageContext();
$request = Request::create(
$this->currentUri, $method, $parameters,
$cookies, $files,
array_replace($this->serverVariables, $server),
$content
);
$response = $kernel->handle($request);
$kernel->terminate($request, $response);
return $this->response = $response;
}
}
and don't forget to switch Illuminate\Http\Request::capture() to App\Http\Request::capture() in public/index.php file and to add $app->alias('request', 'App\Http\Request'); after or inside $app = require_once __DIR__.'/../bootstrap/app.php';
Yerkes answer inspired me to write a custom class, for use with pagination, but only on specific requests
<?php
namespace App\Http\Requests;
use Illuminate\Http\Request;
class PaginatedRequest extends Request
{
public function page(): int
{
return max(1, (int) ($this['page'] ?? 1));
}
public function perPage(): int
{
$perPage = (int) ($this['per_page'] ?? 100);
return max(1, min($perPage, 500));
}
public function offset(): int
{
return ($this->page() - 1) * $this->perPage();
}
}
I then also had to register a new ServiceProvider in /config/app.php, which looks like
<?php
namespace App\Providers;
use App\Http\Requests\PaginatedRequest;
use Illuminate\Support\ServiceProvider;
class PaginatedRequestServiceProvider extends ServiceProvider
{
public function boot()
{
$this->app->resolving(PaginatedRequest::class, function ($request, $app) {
PaginatedRequest::createFrom($app['request'], $request);
});
}
}
Now I can simply inject the PaginatedRequest in my controller methods only when I need it
<?php
namespace App\Http\Controllers;
use App\Http\Requests\PaginatedRequest;
class MyController
{
public function __invoke(PaginatedRequest $request)
{
$request->page();
// ...
}
}
I was able to add custom request object using FormRequest in Laravel 5.5 as follows.
First, just create FormRequest:
php artisan make:request MyRequest
Then just make it look like this:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MyRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
//
];
}
}
You can then use MyRequest as drop-in replacement in any function that takes Request as parameter:
public function get(MyRequest $request)
{
}
I do realize that FormRequests are actually meant to be used for a different purpose, but whatever works.
Documentation on FormRequest: https://laravel.com/docs/5.0/validation#form-request-validation
In Laravel 5.5.x, I was able to extend the Request class for specific requests using the following:
ExtendedRequest class
<?php declare(strict_types = 1);
namespace App\Http\Request;
use Illuminate\Http\Request;
class ExtendedRequest extends Request {
public function hello() {
echo 'hello world!';
}
}
ExtendedRequestServiceProvider
<?php declare(strict_types = 1);
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Http\Request\ExtendedRequest;
class ExtendedRequestServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(ExtendedRequest::class, function ($app) {
return ExtendedRequest::capture();
});
}
}
Then in your Controllers
<?php
namespace App\Controllers;
use App\Http\Request\ExtendedRequest;
class TestController extends Controller
{
public function get(ExtendedRequest $request) {
echo $request->hello();
}
}
Hope this helps someone.

Laravel4 init another Model from a Model

How do initiate model from model in Laravel 4. I try the following but without any success
namespace App\Models;
use \Httpful\Request;
use \RecursiveIteratorIterator;
use \RecursiveArrayIterator;
use Illuminate\Support\Facades\DB as DB;
class Parser extends \Eloquent {
public function __construct(AffiApi $affiapi) {
$this->affiapi = $affiapi;
$url = "HTTP://some.com/xml/feed_config.xml";
$xml = Request::get($url)->expectsXml()->send();
$this->xml = $xml->body;
}
public function Test(){
$api = new Api();
//I get error Class 'App\Models\Api' not found
}
}
class Api extends Parser {
}
How di i do it properly or what is the cleanest way to achive this
The neatest and best practiced way to do this is by using a principle called dependency injection (DI). This way it is loosely coupled and it will also be possible to inject stub classes for unit testing.
class Parser extends \Eloquent {
public function __construct(Api $api) {
$this->api = $api;
}
public function Test() {
}
}
Be sure to run php artisan dump-autoload from the root of your project after adding new classes.

Categories