I have this Controller :
namespace FacilitaTripBundle\Controller;
use FacilitaTripBundle\Api\models\GuideDestination\GuideDestinationModel;
use FacilitaTripBundle\Api\models\Destination\DestinationModel;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Response;
/**
* #Route("/api/v1", name="api")
*/
class ApiController extends Controller
{
/**
* #Route("/destinations/get_all_minimal/", name="destination_get_minimal")
*/
public function getMinimalAction()
{
$model = new DestinationModel();
$data = $model->getAllMinimal();
$response = new Response(json_encode($data));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
/**
* #Route("/guideDestination/getLastAddDestination/", name="destination_last_add_destination")
*/
public function getLastAddDestinationAction()
{
$guide_destination_model = new GuideDestinationModel();
$data = $guide_destination_model->getLastAddDestination();
$response = new Response(json_encode($data));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
}
I got this error :
Attempted to load class "GuideDestinationModel" from namespace "FacilitaTripBundle\Api\models\GuideDestination".
Did you forget a "use" statement for another namespace
I define the class GuideDestinationModel in this file :
namespace FacilitaTripBundle\Api\models\GuideDestination;
class GuideDestinationModel {
}
How you got an idea why I got this error ?
What is the physical path to file containing GuideDestinationModel class?
According to PSR-0, which Symfony is using, you are supposed to put class definitions in path which corresponds to your namespace like in following example.
\Symfony\Core\Request =>
/path/to/project/lib/vendor/Symfony/Core/Request.php
I notice you have lowercase models in your FacilitaTripBundle\Api\models\GuideDestination namespace. Is your directory models also lowercase? If not this might be the issue here if you are using system which is case sensitive when it comes to path resolving (i.e. Linux)
Related
In Laravel I need to communicate to a 3rd party API. Thay have given me some PHP implementation (class) which I can use to connect and communicate with their API.
But when I try this as a class in a subfolder of the App folder and add this to my controller, I get a class not found error.
I have added a folder 'Qenner' (the provider of the API) in the App folder. And copied their classes in there.
In my controller I'm using these classes and add a code sample, like they send it to me.
Controller code (API-KEY is replaced with the actual key):
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Qenner\Search;
use QennerSearch\ServiceClient;
class TestController extends Controller
{
public function index() {
$search = new Search('https://search.qenner.com', 'API-KEY', true, 'nl-NL');
$response = $search->getCriteria([], ['Country'], []);
if (!$response->isError()) {
$criterionSets = $response->getCriterionSets();
$countryCriterionSet = criterionSets[0];
$countries = $countryCriterionSet->getCriteria();
$resultCount = $response->getResultCount();
}
dd($response);
}
Search.php in Qenner folder:
/**
* #file
* Contains QennerSearch\Search.
*/
namespace QennerSearch;
use QennerSearch\model\messages\CriterionTypesResponse;
use QennerSearch\model\messages\CriteriaRequest;
use QennerSearch\model\messages\CriteriaResponse;
use QennerSearch\model\messages\ErrorResponse;
use QennerSearch\model\messages\SearchRequest;
use QennerSearch\model\messages\SearchResponse;
use QennerSearch\model\messages\PriceRequest;
use QennerSearch\model\messages\PriceResponse;
use QennerSearch\model\messages\AccommodationInfoRequest;
use QennerSearch\model\messages\AccommodationInfoResponse;
use QennerSearch\model\messages\AutoCompleteRequest;
use QennerSearch\model\messages\AutoCompleteResponse;
/**
* Class Search, using ServiceClient to communicate, implementing the SearchInterface
*
* #package QennerSearch
*/
class Search extends ServiceClient implements SearchInterface {
.....
The folder has a ServiceClient.php
ServiceClient.php
/**
* #file
* Contains QennerSearch\Search.
*/
namespace QennerSearch;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
/**
* Class Search.
*
* #package QennerSearch
*/
class ServiceClient {
protected $http_client = null;
protected $engine_url = null;
protected $api_key = null;
protected $log_calls = false;
protected $locale = null;
protected $last_result_code = 0;
protected $last_error_body = null;
public function __construct($engine_url, $api_key, $log_calls = false, $locale = "nl-NL") {
$this->http_client = new Client();
$this->engine_url = $engine_url;
$this->api_key = $api_key;
$this->log_calls = $log_calls;
$this->locale = $locale;
I get this error:
Class 'QennerSearch\ServiceClient' not found
While I expected a dump of the output
Updated
After seeing your folder structure in the comments, I believe ServiceClient.php and Search.php, both are inside the folder: app\Qenner, hence inside those files:
wherever you are using: namespace QennerSearch;
you should use: namespace App\Qenner;
and then inside your controller, instead of using: use QennerSearch\ServiceClient;
use: namespace App\Qenner\ServiceClient
Namespaces are not like aliases, they need to reflect the position of the file itself if that makes sense.
Please give it a try and let me know if it works.
I am using Symfony2 with the FOSRestBundle. Is it possible to have the functionality of the #QueryParam and #RequestParam annotations without using annotations?
I am trying to build a json api (format), so I want to allow query params like include, page, filter, fields, and sort. My ideal way to handle this would be:
Use the format_listener to detect it is json.
Use a custom body_listener json handler to process the request so that it's similar to this.
Have the controller validate the query/request params inside the action function, and throw an exception to be handled by the exception controller if it's invalid. (The body_listener would act as a helper to make extracting the data from the request easier in the controller, but the controller makes the decisions of what to do with that data.)
I'm mostly stuck on how to make a custom body_listener. I'm not sure if I would need to make a custom decoder or normalizer, and what that class might look like since they don't give any examples.
Rough code of what controller would look like:
<?php
namespace CoreBundle\Controller;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\View\View;
use FOS\RestBundle\Context\Context;
use Psr\Http\Message\ServerRequestInterface;
class SiteController extends FOSRestController
{
public function getAction($id, ServerRequestInterface $request)
{
try {
// Validate $request. This is where the query/request
// param annotation functionality would be replaced.
} catch (Exception $e) {
throw new InvalidRequestException($e);
}
$siteService = $this->get('app.site_service');
$site = $siteService->getSite($id);
$context = new Context();
$context->setVersion($request->getVersion());
// Ex: /sites/63?fields[sites]=name,address&fields[company]=foo,bar
if ($request->hasIncludeFields()) {
$context->addAttribute('include_fields', $request->getIncludeFields()); // Or however to do this
}
$view = new View($site, 200);
$view->setContext($context);
return $view;
}
}
You can define parameters dynamically in param fetcher. It's described in documentation.
For example:
With annotations:
<?php
namespace ContentBundle\Controller\API;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Controller\Annotations\QueryParam;
use FOS\RestBundle\Request\ParamFetcher;
class PostController extends FOSRestController
{
/**
* #QueryParam(name="sort", requirements="(asc|desc)", allowBlank=false, default="asc", description="Sort direction")
*/
public function getPostsAction(ParamFetcher $paramFetcher)
{
$sort = $paramFetcher->get('sort');
}
}
Without annotations:
<?php
namespace ContentBundle\Controller\API;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Controller\Annotations\QueryParam;
use FOS\RestBundle\Request\ParamFetcher;
class PostController extends FOSRestController
{
public function getPostsAction(ParamFetcher $paramFetcher)
{
$sort = new QueryParam();
$sort->name = 'sort';
$sort->requirements = '(asc|desc)';
$sort->allowBlank = false;
$sort->default = 'asc';
$sort->description = 'Sort direction';
$paramFetcher->addParam($sort);
$param = $paramFetcher->get('sort');
//
}
}
I would like to know if there is a magic method to use this scenario :
If I call a page via an AJAX request the controller returns a JSON object, otherwise it returns a view, i'm trying to do this on all my controllers without changin each method.
for example i know that i can do this :
if (Request::ajax()) return compact($object1, $object2);
else return view('template', compact($object, $object2));
but I have a lot of controllers/methods, and I prefer to change the basic behavior instead of spending my time to change all of them. any Idea ?
The easiest way would be to make a method that is shared between all of your controllers.
Example:
This is your controller class that all other controllers extend:
<?php namespace App\Http\Controllers;
use Illuminate\Routing\Controller as BaseController;
abstract class Controller extends BaseController
{
protected function makeResponse($template, $objects = [])
{
if (\Request::ajax()) {
return json_encode($objects);
}
return view($template, $objects);
}
}
And this is one of the controllers extending it:
<?php namespace App\Http\Controllers;
class MyController extends Controller
{
public function index()
{
$object = new Object1;
$object2 = new Object2;
return $this->makeResponse($template, compact($object, $object2));
}
}
Update for Laravel 5+
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
protected function makeResponse($request, $template, $data = [])
{
if ($request->ajax()) {
return response()->json($data);
}
return view($template, $data);
}
}
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class MyController extends Controller
{
public function index(Request $request)
{
$object = new Object1;
$object2 = new Object2;
return $this->makeResponse($request, $template, compact($object, $object2));
}
}
There is no magic but you can easily override ViewService in 3 steps:
1.create your view factory (your_project_path/app/MyViewFactory.php)
<?php
/**
* Created by PhpStorm.
* User: panos
* Date: 5/2/15
* Time: 1:35 AM
*/
namespace App;
use Illuminate\View\Factory;
class MyViewFactory extends Factory {
public function make($view, $data = array(), $mergeData = array())
{
if (\Request::ajax()) {
return $data;
}
return parent::make($view, $data, $mergeData);
}
}
2.create your view service provider (your_project_path/app/providers/MyViewProvider.php)
<?php namespace App\Providers;
use App\MyViewFactory;
use Illuminate\View\ViewServiceProvider;
class MyViewProvider extends ViewServiceProvider {
/**
* Register the application services.
*
* #return void
*/
public function register()
{
parent::register();
}
/**
* Overwrite original so we can register MyViewFactory
*
* #return void
*/
public function registerFactory()
{
$this->app->singleton('view', function($app)
{
// Next we need to grab the engine resolver instance that will be used by the
// environment. The resolver will be used by an environment to get each of
// the various engine implementations such as plain PHP or Blade engine.
$resolver = $app['view.engine.resolver'];
$finder = $app['view.finder'];
// IMPORTANT in next line you should use your ViewFactory
$env = new MyViewFactory($resolver, $finder, $app['events']);
// We will also set the container instance on this view environment since the
// view composers may be classes registered in the container, which allows
// for great testable, flexible composers for the application developer.
$env->setContainer($app);
$env->share('app', $app);
return $env;
});
}
}
3.in your_project_path/config/app.php:
change 'Illuminate\View\ViewServiceProvider',
to 'App\Providers\MyViewProvider',
What this do:
it tells your application to use another view provider which will register your view factory
$env = new MyViewFactory($resolver, $finder, $app['events']);
in line 33 of MyViewProvider.php which will check if request is AJAX and return if true or continue with original behavior
return parent::make($view, $data, $mergeData);
in MyViewFactory.php line 19
Hope this help you,
In laravel 5.1, this is the best way:
if (\Illuminate\Support\Facades\Request::ajax())
return response()->json(compact($object1, $object2));
else
return view('template', compact($object, $object2));
The solution suggested by #ryanwinchester is really good. I, however, wanted to use it for the responses from update() and delete(), and there naturally return view() at the end doesn't make a lot of sense as you mostly want to use return redirect()->route('whatever.your.route.is'). I thus came up with that idea:
// App\Controller.php
/**
* Checks whether request is ajax or not and returns accordingly
*
* #param array $data
* #return mixed
*/
protected function forAjax($data = [])
{
if (request()->ajax()) {
return response()->json($data);
}
return false;
}
// any other controller, e.g. PostController.php
public function destroy(Post $post)
{
// all stuff that you need until delete, e.g. permission check
$comment->delete();
$r = ['success' => 'Wohoo! You deleted that post!']; // if necessary
// checks whether AJAX response is required and if not returns a redirect
return $this->forAjax($r) ?: redirect()->route('...')->with($r);
}
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 am very new to PHP namespaces and I have a question:
If we import multiple namespaces with the 'use' command, shouldn't we run into issues?
I have been reading through this post http://www.sitepoint.com/php-namespaces-import-alias-resolution/, and in the section about Namespace Importing, it says that you still need to have a qualified prefix next to a function (ie: Lib2), but when looking at some Symfony 2 example files, I don't see that happening.
How is namespacing solving (in the example file below) solving name conflicts?
namespace Acme\DemoBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Acme\DemoBundle\Form\ContactType;
// these import the "#Route" and "#Template" annotations
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class DemoController extends Controller
{
/**
* #Route("/", name="_demo")
* #Template()
*/
public function indexAction()
{
return array();
}
/**
* #Route("/hello/{name}", name="_demo_hello")
* #Template()
*/
public function helloAction($name)
{
return array('name' => $name);
}
/**
* #Route("/contact", name="_demo_contact")
* #Template()
*/
public function contactAction(Request $request)
{
$form = $this->createForm(new ContactType());
$form->handleRequest($request);
if ($form->isValid()) {
$mailer = $this->get('mailer');
// .. setup a message and send it
// http://symfony.com/doc/current/cookbook/email.html
$request->getSession()->getFlashBag()->set('notice', 'Message sent!');
return new RedirectResponse($this->generateUrl('_demo'));
}
return array('form' => $form->createView());
}
}
I think the misunderstanding comes from the post's filenames. keep in mind:
DefaultController.php with namespace Acme/AcmeBundle/Controller/ will be included with use Acme/AcmeBundle/Controller/DefaultController
DefaultController2.php with namespace Acme/AcmeBundle/Controller2/ will be included with use Acme/AcmeBundle/Controller2/DefaultController2
To have a conflict you'd need to include two files named DefaultController.php
DefaultController.php with namespace Acme/AcmeBundle/Controller
DefaultController.php with namespace Acme/AcmeBundle/Controller2
And would then need an alias as pointed out by Kevin
DefaultController.php with namespace Acme/AcmeBundle/Controller
=> use Acme/AcmeBundle/Controller/DefaultController
DefaultController.php with namespace Acme/AcmeBundle/Controller2
=> use Acme/AcmeBundle/Controller2/DefaultController as DefaultController2
Maybe the best way to explain this is to work backwards from the namespace resolution.
Say you have a line like the following:
$newFoo = new Foo();
PHP will look through the current symbol table for the class Foo. That symbol table includes the current namespace and any imported (use) namespaces (as well as built-in PHP symbols).
For example, say class Foo is declared in Foo.php:
<?php //Baz/Foo.php
namespace Baz\Foo;
class Foo{}
If you were working in Bar.php, you could reference the following:
<?php //Baz/Bar.php
namespace Baz\Bar;
use Baz\Foo;
//The following lines are equivalent
$newFoo = new Foo\Foo(); //Local namespace
$newFoo = new \Baz\Foo\Foo(); //Fully qualified namespace
You could also reference the Foo class directly in your use, rather than referencing the Foo namespace.
<?php //Baz/Bar.php
namespace Baz\Bar;
use Baz\Foo\Foo;
//The following lines are equivalent
$newFoo = new Foo(); //Local namespace
//$newFoo = new Foo\Foo(); //This will fail, as the symbol Foo is a class, not a namespace
$newFoo = new \Baz\Foo\Foo(); //Fully qualified namespace still works.
If you wanted to reference both the Namespace and the Class Foo directly, you'd need to resolve the conflict with an Alias.
<?php //Baz/Bar.php
namespace Baz\Bar;
use Baz\Foo;
use Baz\Foo\Foo as FooClass;
//The following lines are equivalent
$newFoo = new FooClass(); //Notice the new aliased symbol.
$newFoo = new Foo\Foo(); //This works now.
$newFoo = new \Baz\Foo\Foo(); //Fully qualified namespace still works.
//$newFoo = new \Baz\Foo\FooClass(); //This will fail, aliases don't work for fully qualified names.
Also, say, in addition to your Baz/Foo.php file, you have a Bar/Foo.php file as such:
<?php //Bar/Foo.php
namespace Bar\Foo;
class Foo{}
If you wanted to reference both namespaces locally, you'll need aliases to resolve the name conflict:
<?php //Baz/Bar.php
namespace Baz\Bar;
use Baz\Foo as Foo1;
use Baz\Foo\Foo as Foo1Class;
use Bar\Foo as Foo2;
use Bar\Foo\Foo as Foo2Class;
//The following lines are equivalent, referencing Baz\Foo\Foo
$newBazFoo = new Foo1Class(); //Object alias.
$newBazFoo = new Foo1\Foo(); //Namespace alias.
$newBazFoo = new \Baz\Foo\Foo(); //Fully qualified namespaces don't need any aliases.
//The following lines are equivalent, referencing Bar\Foo\Foo
$newBarFoo = new Foo2Class(); //Object alias.
$newBarFoo = new Foo2\Foo(); //Namespace alias.
$newBarFoo = new \Bar\Foo\Foo(); //Fully qualified namespaces don't need any aliases.
In addition to importing the Namespace with the use statement, you will also need to include the file containing that namespace. This can be done manually with require_once(), but is normally handled automatically by an autoloader.
See Symfony Class Loader for more details.
Hope that helps.