Just created a new project with symfony2 and installed FOSRestBundle.
Controller example:
<?php
namespace ApiBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use FOS\RestBundle\Controller\FOSRestController;
class TestController extends FOSRestController
{
public function TestAction()
{
$data = array ('1', '2', '3', 'four'); // get data, in this case list of users.
$view = $this->view($data, 200)
->setTemplate("ApiBundle:Test:test.html.twig")
->setTemplateVar('test')
;
return $this->handleView($view);
}
}
Got error message:
You have requested a non-existent service "fos_rest.view_handler".
Does anyone have an idea about this?
In your AppKernel, be sure you have the following:
// app/config/AppKernel.php
public function registerBundles()
{
$bundles = array(
// ...
new FOS\RestBundle\FOSRestBundle(),
);
}
You surely forgotten this step.
Related
I am trying to plug APCu into the Symfony 3.3 test project.
I am getting an error, when I add ApcuAdapter to AppKernel.php.
Here is the list of what I have done:
in ./app/AppKernel.php i have added a "new" line to $bundles in public function registerBundles():
public function registerBundles()
{
$bundles = [
... ,
new Symfony\Component\Cache\Adapter\ApcuAdapter()
];
...
return $bundles;
}
Opened the project's site. It shows an error:
Attempted to call an undefined method named "getName" of class "Symfony\Component\Cache\Adapter\ApcuAdapter".
(./ means the root folder of the project)
Please, tell me why does this error happen and how to plug this adapter into the symfony framework. Thank you.
me have found the solution somewhere on the framework's website.
somehow, we should use not the Adapter, but the Simple instead.
seems very un-logical to me.
so, the Service now works and looks this way:
<?php
namespace AppBundle\Service;
use Symfony\Component\Cache\Simple\ApcuCache;
class ApcuTester
{
public function __construct
(
)
{
}
public function testMe()
{
$cache = new ApcuCache();
$TestVar_dv = 0;
$TestVar_vn = 'TestVar';
$TestVar = NULL;
//$cache-> deleteItem($TestVar_vn); // dbg
// Read
if ( $cache->hasItem($TestVar_vn) )
{
$TestVar = $cache->get($TestVar_vn);
}
else
{
$cache->set($TestVar_vn, $TestVar_dv);
$TestVar = $TestVar_dv;
}
// Modify
$TestVar++;
// Save
$cache->set($TestVar_vn, $TestVar);
// Return
return $TestVar;
}
}
And the Controller which executes this Service looks as this:
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use AppBundle\Service\MessageGenerator;
use AppBundle\Service\ApcuTester;
class LuckyController extends Controller
{
/**
* #Route("/lucky/number", name="lucky")
*/
public function numberAction
(
Request $request,
MessageGenerator $messageGenerator,
ApcuTester $apcuTester
)
{
$lucky_number = mt_rand(0, 100);
$message = $messageGenerator->getHappyMessage();
$testvar = $apcuTester->testMe();
$tpl = 'default/lucky_number.html.twig';
$tpl_vars =
[
'lucky_number' => $lucky_number,
'message' => $message,
'testvar' => $testvar
];
return $this->render($tpl, $tpl_vars);
}
}
If i wrote the same thing in pure PHP i would have done it an hour earlier :) Oh these crazy frameworks...
I am new to unit testing and trying to test a controller method in Laravel 5.1 and Mockery.
I am trying to test a registerEmail method I wrote, below:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Response;
use Mailchimp;
use Validator;
/**
* Class ApiController
* #package App\Http\Controllers
*/
class ApiController extends Controller
{
protected $mailchimpListId = null;
protected $mailchimp = null;
public function __construct(Mailchimp $mailchimp)
{
$this->mailchimp = $mailchimp;
$this->mailchimpListId = env('MAILCHIMP_LIST_ID');
}
/**
* #param Request $request
* #return \Illuminate\Http\JsonResponse
*/
public function registerEmail(Request $request)
{
$this->validate($request, [
'email' => 'required|email',
]);
$email = $request->get('email');
try {
$subscribed = $this->mailchimp->lists->subscribe($this->mailchimpListId, [ 'email' => $email ]);
//var_dump($subscribed);
} catch (\Mailchimp_List_AlreadySubscribed $e) {
return Response::json([ 'mailchimpListAlreadySubscribed' => $e->getMessage() ], 422);
} catch (\Mailchimp_Error $e) {
return Response::json([ 'mailchimpError' => $e->getMessage() ], 422);
}
return Response::json([ 'success' => true ]);
}
}
I am attempting to mock the Mailchimp object to work in this situation.
So far, my test looks as follows:
<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class HomeRouteTest extends TestCase
{
use WithoutMiddleware;
public function testMailchimpReturnsDuplicate() {
$listMock = Mockery::mock('Mailchimp_Lists')
->shouldReceive('subscribe')
->once()
->andThrow(\Mailchimp_List_AlreadySubscribed::class);
$mailchimp = Mockery::mock('Mailchimp')->lists = $listMock;
$this->post('/api/register-email', ['email'=>'duplicate#email.com'])->assertJson(
'{"mailchimpListAlreadySubscribed": "duplicate#email.com is already subscribed to the list."}'
);
}
}
I have phpUnit returning a failed test.
HomeRouteTest::testMailchimpReturnsDuplicate
Mockery\Exception\InvalidCountException: Method subscribe() from Mockery_0_Mailchimp_Lists should be called exactly 1 times but called 0 times.
Also, if I assert the status code is 422, phpUnit reports it is receiving a status code 200.
It works fine when I test it manually, but I imagine I am overlooking something fairly easy.
I managed to solve it myself. I eventually moved the subscribe into a seperate Job class, and was able to test that be redefining the Mailchimp class in the test file.
class Mailchimp {
public $lists;
public function __construct($lists) {
$this->lists = $lists;
}
}
class Mailchimp_List_AlreadySubscribed extends Exception {}
And one test
public function testSubscribeToMailchimp() {
// create job
$subscriber = factory(App\Models\Subscriber::class)->create();
$job = new App\Jobs\SubscribeToList($subscriber);
// set up Mailchimp mock
$lists = Mockery::mock()
->shouldReceive('subscribe')
->once()
->andReturn(true)
->getMock();
$mailchimp = new Mailchimp($lists);
// handle job
$job->handle($mailchimp);
// subscriber should be marked subscribed
$this->assertTrue($subscriber->subscribed);
}
Mockery will expect the class being passed in to the controller be a mock object as you can see here in their docs:
class Temperature
{
public function __construct($service)
{
$this->_service = $service;
}
}
Unit Test
$service = m::mock('service');
$service->shouldReceive('readTemp')->times(3)->andReturn(10, 12, 14);
$temperature = new Temperature($service);
In laravel IoC it autoloads the classes and injects them, but since its not autoloading Mailchimp_Lists class it won't be a mock object. Mailchimp is requiring the class atop it's main class require_once 'Mailchimp/Lists.php';
Then Mailchimp is then loading the class automatically in the constructor
$this->lists = new Mailchimp_Lists($this);
I don't think you'll be able to mock that class very easily out of the box. Since there isn't away to pass in the mock object to Mailchimp class and have it replace the instance of the real Mailchimp_Lists
I see you are trying to overwrite the lists member variable with a new Mock before you call the controller. Are you certain that the lists object is being replaced with you mocked one? Try seeing what the classes are in the controller when it gets loaded and see if it is in fact getting overridden.
When I try to create a doctrine crud for an entity I get an 'Unknown entity namespace alias' exception.
I have the following project structure
With a series of bundles (in the Bundles directory) using the Entities in the src\Project\Entity directory.
As you can see my entity namespace is
namespace Project\Entity;
I have a feeling it might be to do with the auto_mapping, but I have been playing with this for 4-5 hours and am getting nowhere.
Any suggestions?
Solution:
Create a command which extends the base doctrine crud command
extends \Sensio\Bundle\GeneratorBundle\Command\GenerateDoctrineCrudCommand
Modifying
$entityClass = $this->getContainer()->get('doctrine')->getAliasNamespace('Project').'\\'.$entity;
to the namespace of the entity. By default it assumes the entity is in the Bundle where you want the CRUD to be created.
By setting
$this->setName('project:generate:crud');
in the Configre() function you can call the function from your command line
Example:
<?php
namespace Project\Bundle\UtilityBundle\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Sensio\Bundle\GeneratorBundle\Command\Validators;
class GenerateDoctrineCrudCommand extends \Sensio\Bundle\GeneratorBundle\Command\GenerateDoctrineCrudCommand
{
protected function configure()
{
parent::configure();
$this->setName('project:generate:crud');
$this->setDescription('CRUD generator that supports entities outside a bundle');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$questionHelper = $this->getQuestionHelper();
if ($input->isInteractive()) {
$question = new ConfirmationQuestion($questionHelper->getQuestion('Do you confirm generation', 'yes', '?'), true);
if (!$questionHelper->ask($input, $output, $question)) {
$output->writeln('<error>Command aborted</error>');
return 1;
}
}
// Note: this expects an argument like InterpracCorporateFrontendBundle:Notification
list($bundle, $entity) = explode(':', $input->getOption('entity'));
$format = Validators::validateFormat($input->getOption('format'));
$prefix = $this->getRoutePrefix($input, $entity);
$withWrite = $input->getOption('with-write');
$forceOverwrite = $input->getOption('overwrite');
$questionHelper->writeSection($output, 'CRUD generation');
$entityClass = $this->getContainer()->get('doctrine')->getAliasNamespace('Project').'\\'.$entity;
$metadata = $this->getEntityMetadata($entityClass);
$bundle = $this->getContainer()->get('kernel')->getBundle($bundle);
$generator = $this->getGenerator($bundle);
$generator->generate($bundle, $entity, $metadata[0], $format, $prefix, $withWrite, $forceOverwrite);
$output->writeln('Generating the CRUD code: <info>OK</info>');
$errors = array();
$runner = $questionHelper->getRunner($output, $errors);
// form
if ($withWrite) {
$output->write('Generating the Form code: ');
if ($this->generateForm($bundle, $entity, $metadata)) {
$output->writeln('<info>OK</info>');
} else {
$output->writeln('<warning>Already exists, skipping</warning>');
}
}
// routing
if ('annotation' != $format) {
$runner($this->updateRouting($questionHelper, $input, $output, $bundle, $format, $entity, $prefix));
}
$questionHelper->writeGeneratorSummary($output, $errors);
}
}
The problem is that you are keeping your entities outside bundles. Since this is not standard behaviour, you have to extend or create another GenerateDoctrineCrudCommand to be able to pass namespace alias.
I would like to override the method setContent() of the class Illuminate\Http\Response as :
<?php
namespace MyNameSpace\MyClass;
use Illuminate\Http\Response;
class myResponse extends Reponse {
public function setContent($content)
// Something
}
}
But I don't know how to tell Laravel to load my class instead of the original one.
Too late, but as i came up with same issue. but for reference i would want to post how i resolved this issue.
When i wanted to handle all the the response by myself without using a response macro or transformer and TO OVERRIDE MANY OTHER FRAMEWORK DEFAULT METHODs . this is how i completely took control of the response object.
just posted here for reference as i in my opinion it solves the
problem in a clearer way.
Lot of overriding as its done through pipeline and routing and so it`s registered as base service provider. here is how i managed to override all.
here I am using laravel 5.3
1 - create a new response class
<?php
namespace App\Extensions\Illuminate\Http;
// use Illuminate\Http\Response as BaseResponse;
use Symfony\Component\HttpFoundation\Response as BaseResponse;
class Response extends BaseResponse
{
public function setContent($content)
{
//do what ever you want to do with the content
//dd($content);
}
}
2 - create a new router and use the new response
<?php
namespace App\Extensions\Illuminate\Routing;
use Illuminate\Http\Request;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Routing\Router as IlluminateRouter;
use App\Extensions\Illuminate\Http\Response;
class Router extends IlluminateRouter
{
public function prepareResponse($request, $response)
{
if ($response instanceof PsrResponseInterface) {
$response = (new HttpFoundationFactory)->createResponse($response);
} elseif (! $response instanceof SymfonyResponse) {
$response = new Response($response);
}
return $response->prepare($request);
}
}
3 - create new routing service provider use new router
<?php
namespace App\Providers;
use Illuminate\Routing\RoutingServiceProvider as ServiceProvider;
use App\Extensions\Illuminate\Routing\Router;
class RoutingServiceProvider extends ServiceProvider
{
protected function registerRouter()
{
$this->app['router'] = $this->app->share(function ($app) {
return new Router($app['events'], $app);
});
}
}
4 - create new Application class and use new routing service provider
<?php
namespace App\Extensions\Illuminate\Foundation;
use Illuminate\Events\EventServiceProvider;
use Illuminate\Foundation\Application as App;
use App\Providers\RoutingServiceProvider;
class Application extends App
{
protected function registerBaseServiceProviders()
{
$this->register(new EventServiceProvider($this));
$this->register(new RoutingServiceProvider($this));
}
}
5 - and finally in bootstrap\app.php use the new Application
// $app = new Illuminate\Foundation\Application(
// realpath(__DIR__.'/../')
// );
$app = new App\Extensions\Illuminate\Foundation\Application(
realpath(__DIR__.'/../')
);
You will need to extend the Response facade to reflect the class you have, then change your applications /config/app.php to link to your new facade rather than laravels.
You need to create a facade like so
<?php namespace App\Facades;
use Illuminate\Support\Facades\Response as BaseResponse;
class Response extends BaseResponse {
public static function overwriteMethod()
{
//
}
}
Then go to config/app.php under facades comment out this line
//'Response' => 'Illuminate\Support\Facades\Response',
Then add this to the facades stack
'Response' => 'App\Facades\Response',
I have the problem with doctrine console. I have a MailServiceFactory.php containing this code:
namespace Application\Service;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class MailServiceFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$I_render = $serviceLocator->get('ViewRenderer');
$a_config = $serviceLocator->get('config');
return new MailService($I_render, $a_config);
}
}
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class MailServiceFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$I_render = $serviceLocator->get('ViewRenderer');
$a_config = $serviceLocator->get('config');
return new MailService($I_render, $a_config);
}
}
and all functions works, but when i execute the vendor/bin/doctirne-module I get the error below:
Fatal error: Uncaught exception
'Zend\ServiceManager\Exception\ServiceNotFoundException' with message
'Zend\ServiceManager\ServiceManager::get was unable to fetch or create
an instance for ViewRenderer' in
/Users/Daniele/Apps/corradini.com/www/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:529
Why i'm getting this error?
You cannot get ViewRenderer directly from ServiceLocator on console requests because its not in stage since request is not an HttpRequest.
Instead of ViewRenderer, you can easily try to passing a PhpRenderer instance to your service and render a ViewModel using that. For example:
use Zend\View\Renderer\PhpRenderer;
class MailServiceFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$renderer = new PhpRenderer();
$a_config = $serviceLocator->get('config');
return new MailService($renderer, $a_config);
}
Now in your MailService:
use Zend\View\Renderer\RendererInterface;
use Zend\View\Model\ViewModel;
class MailService
{
protected $renderer;
public function __construct(RendererInterface $renderer, $config)
{
$this->renderer = $renderer;
// set your config etc..
}
public function fooMethod()
{
$model = new ViewModel();
$model->setTemplate('path/to/template.phtml');
$result = $this->renderer->render($model);
// The $result contains rendered html markup
}
Hope it helps.
Thanks foozy now doctrine console funciton! but the i can't read the tempalte for render mail.
My tempate is locatend in module/Application/view/email/template.phtml
In my module.config.php there is configuration:
'template_path_stack' => array(
'application' => __DIR__ . '/../view',
),
And error:
Zend\View\Renderer\PhpRenderer::render: Unable to render template "email/template.phtml"; resolver could not resolve to a file
where mistake ?
thanks
I had the same problem after I added a view_manager config to the console config. The solution is to just pull the renderer from the ViewManager:
$renderer = $serviceLocator->get('ViewManager')->getRenderer();
This will also register the ViewRenderer service, so any calls to get('ViewRenderer') after this should work too.
in ZF3 also a TemplateMapResolver can help you to fix some issues like:
"PhpRenderer::render: Unable to render template"
$resolver = new \Zend\View\Resolver\TemplateMapResolver();
$resolver->setMap([
'mailTemplate' => __DIR__ . '/../../view/email/testmail.phtml'
]);
$renderer->setResolver($resolver);
$viewContent = new \Zend\View\Model\ViewModel($contentArray);
$viewContent->setTemplate('mailTemplate');