I have a class
class Contact {
public function searchByPhone(ServerRequestInterface $request, BaseResponse $response, $args) {
return 'somedata';
}
}
Slim framework uses this method
$app->get("/searchByPhone/2342", Contact::class . ':searchByPhone');
I want use
$app->get("/searchByPhone/2342", Contact::searchByPhone());
So that when I click on a function NetBeans will take me to the right place in the code.
How do I do this?
You could try to use a Single Action Controller with a single __invoke method.
Example
<?php
namespace App\Action;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
final class ContactPhoneSearchAction
{
public function __invoke(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface {
$params = (array)$request->getQueryParams();
$phone = (string)$params['phone'];
// Invoke service method
// ...
// Build response
$response->getBody()->write('Hello, World!');
return $response;
}
}
Usage
$app->get('/contacts/phone', \App\Action\ContactPhoneSearchAction::class);
Related
I am implementing the Repository Pattern (service) in a Laravel application and I have some doubts about the usage of interfaces with these services.
I have created an interface called CRUD (code bellow) to serve as a way to always keep the same names for the services that are going to implement CRUD methods.
<?php
namespace App\Interfaces;
interface CRUD
{
public function create(array $data);
public function update(int $id, array $data);
public function delete(string $ids);
};
Bellow there's an example of how I call my service and the service itself, and that's where my doubts are. Usually I'll see people witing an interface for each service and demanding the controller to have injected an objet of that type. Because of that, people will have to bind a specific type (interface) to the controller. It seems redundant and thus I simply passed the service I need.
Now, is this ok or I should pass the CRUD interface to the controller in this case? Or should I even create another interface specifically for each service?
<?php
namespace App\Http\Controllers\Cms;
use App\Http\Controllers\Controller;
use App\Http\Requests\GroupRequest;
use App\Models\Group;
use App\Services\GroupsService;
use Illuminate\Http\Request;
class GroupsController extends Controller
{
private $service;
public function __construct(GroupsService $service)
{
$this->service = $service;
}
public function store(GroupRequest $request)
{
$result = $this->service->create($request->all());
return redirect()->back()->with('response', $result);
}
public function update(GroupRequest $request, $id)
{
$result = $this->service->update($id, $request->all());
return redirect()->back()->with('response', $result);
}
public function destroy($groups_id)
{
$result = $this->service->delete($groups_id);
return redirect()->back()->with('response', $result);
}
}
<?php
namespace App\Services;
use App\Models\Group;
use App\Interfaces\CRUD;
use Exception;
class GroupsService implements CRUD
{
public function listAll()
{
return Group::all();
}
public function create(array $data)
{
$modules_id = array_pop($data);
$group = Group::create($data);
$group->modules()->attach($modules_id);
return cms_response(trans('cms.groups.success_create'));
}
public function update(int $id, array $data)
{
try {
$modules_ids = $data['modules'];
unset($data['modules']);
$group = $this->__findOrFail($id);
$group->update($data);
$group->modules()->sync($modules_ids);
return cms_response(trans('cms.groups.success_update'));
} catch (\Throwable $th) {
return cms_response($th->getMessage(), false, 400);
}
}
public function delete(string $ids)
{
Group::whereIn('id', json_decode($ids))->delete();
return cms_response(trans('cms.groups.success_delete'));
}
private function __findOrFail(int $id)
{
$group = Group::find($id);
if ($group instanceof Group) {
return $group;
}
throw new Exception(trans('cms.groups.error_not_found'));
}
}
If you want to use Repository Design Patteren You have to create seprate Interface for each service accroing to SOLID Principle. You have to create custom service provider and register your interface and service class and then inject interface in construtor of controller.
You can also follow below article.
https://itnext.io/repository-design-pattern-done-right-in-laravel-d177b5fa75d4
I did something with repo pattern in laravel 8 you might be interested:
thats how i did it:
first of all, you need to implement a provider
in this file i created the binding:
App\ProvidersRepositoryServiceProvider.php
use App\Interfaces\EventStreamRepositoryInterface;
use App\Repositories\EventStreamRepository;
use Illuminate\Support\ServiceProvider;
class RepositoryServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(EventStreamRepositoryInterface::class, EventStreamRepository::class);
}
}
then in file:
app\Interfaces\EventStreamRepositoryInterface.php
interface EventStreamRepositoryInterface {
public function index();
public function create( Request $request );
public function delete($id);
}
in file:
App\Repositories\EventStreamRepository.php
class EventStreamRepository implements EventStreamRepositoryInterface{
public function index()
{
return EventStream::with(['sessions'])
->where([ ["status", "=", 1] ] )
->orderBy('created_at', 'DESC')
->get();
}
public function create(Request $request)
{
request()->validate([
"data1" => "required",
"data2" => "required"
]);
$EventStream = EventStream::create([
'data1' => request("data1"),
'data2' => request('data2')
]);
return $EventStream->id;
}
public function delete($id)
{
return EventStream::where('id', $id)->delete();
}
}
in file:
App\Http\Controllers\EventStreamController.php
use App\Interfaces\EventStreamRepositoryInterface;
class EventStreamController extends Controller{
private EventStreamRepositoryInterface $eventStreamRepository;
public function __construct(EventStreamRepositoryInterface $eventStreamRepository)
{
$this->eventStreamRepository = $eventStreamRepository;
}
public function index():JsonResponse
{
$this->eventStreamRepository->index();
}
public function store(Request $request ):JsonResponse
{
$this->eventStreamRepository->create($request);
}
public function destroy($id):JsonResponse
{
$this->eventStreamRepository->delete($id);
}
}//class
note: i think i removed all unnecessary -validations- and -returns- in controller for better reading.
Hope it helps!!
I am new to Laravel. I have some functions in PaymentController. I want to call those functions from SmartpaySController. Here is the function which is available in PaymentController. Help me to call that function by staying in SmartpaySController.
public function getPaymentFailed($paymentId) {
$transactionData = $this->paymentRepo->find($paymentId);
if($transactionData) {
$data['quote'] = $this->quoteRepo->getQuoteById($transactionData->quote_id);
$data['metaTitle'] = 'Payment failed';
$data['returnMessage'] = $transactionData->return_message;
return view('payment::payment.quote_payment_failed', $data);
}
}
Thank you.
Instead of calling controller methods, the better practice is that you can create traits like: app/Traits and extend in controller
//trait
trait traitName {
public function getData() {
// .....
}
}
//Controller
class ControlelrName extends Controller {
use TraitName;
}
I recomend you to not call functions from one controller to another.
Make Helpers, Resources or implement same feature in other way
Never use controllers as object
But if you want to do it anyway you can use:
SomeController.php
class SomeController extend Controller {
public function someFunction(Request $request) {
// Here Some Code
}
}
YourController.php
use SomeController;
...
public function getPaymentFailed(Request $request, $paymentId) {
$controller_data = (new SomeController)->someFunction($request);
$transactionData = $this->paymentRepo->find($paymentId);
if($transactionData) {
$data['quote'] = $this->quoteRepo->getQuoteById($transactionData->quote_id);
$data['metaTitle'] = 'Payment failed';
$data['returnMessage'] = $transactionData->return_message;
return view('payment::payment.quote_payment_failed', $data);
}
}
Change:
public function getPaymentFailed($paymentId)
to:
public static function getPaymentFailed($paymentId)
This will make it staticly available in your SmartpaySController by doing:
PaymentController::getPaymentFailed($paymentId);
You can make use of Real-Time Facades
Using real-time facades, you may treat any class in your application
as if it were a facade.
To generate a real-time facade, prefix the namespace of the imported
class with Facades:
//...
use use Facades\App\Http\Controllers\SomeController;
//...
return SomeController::getPaymentFailed($request, $paymentId);
I'm using a controller/middleware build with slim 3 and i want from the middleware attached to a group, to pass some data to the $args parameter in my controller - action.
Here's some code:
class MyController
{
protected $container;
public function __construct(ContainerInterface $container) {
$this->container = $container;
}
public function index(Request $request, Response $response, $args) {
return $this->container->get('renderer')->render($response, "index.html.twig", $args);
}
}
class MyMiddleware
{
public function __invoke(Request $request, Response $response, $next)
{
// do some stuff to inject further down to $args some data
return $next($request, $response);
}
}
$app->group('/group', function () use ($app){
//routes
})->add(new MyMiddleware());
My use case is to send stuff to all the views rendered by the actions of these controllers, so i'm also fine with other ways to do this :)
Thanks.
so you need just pass data from Middleware to Controller
what about
class MyMiddleware
{
public function __invoke(Request $request, Response $response, $next)
{
$request = $request->withAttribute('myMagicArgument', 42);
return $next($request, $response);
}
}
and then in controller
class MyController
{
//...
public function index(Request $request, Response $response) {
$yourAttributeFromMiddleware = $request->getAttribute('myMagicArgument');
//...
}
}
For completeness, I'm going to extend the excellent answer given #jDolba
Unfortunately, though it got me going in the right direction, it still took a little experimentation to get everything working.
Basically, as explained in the slim router docs
The route callback signature is determined by a route strategy. By
default, Slim expects route callbacks to accept the request, response,
and an array of route placeholder arguments. This is called the
RequestResponse strategy. However, you can change the expected route
callback signature by simply using a different strategy. As an
example, Slim provides an alternative strategy called
RequestResponseArgs that accepts request and response, plus each route
placeholder as a separate argument. Here is an example of using this
alternative strategy; simply replace the foundHandler dependency
provided by the default \Slim\Container:
$c = new \Slim\Container();
$c['foundHandler'] = function() {
return new \Slim\Handlers\Strategies\RequestResponseArgs();
};
$app = new \Slim\App($c);
$app->get('/hello/{name}', function ($request, $response, $name) {
return $response->write($name);
});
You can provide your own route strategy by implementing the
\Slim\Interfaces\InvocationStrategyInterface.
however, for the task of injecting some standardised data into the $args[] array, the default \Slim\Handlers\Strategies\RequestResponse class does everything it needs to minus injecting the data.
As such, I simply extended that class:
<?php
namespace MyProject\Handlers\Strategies;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use \Slim\Handlers\Strategies\RequestResponse;
class SomeNewInvocationStrategy extends RequestResponse
{
/**
* Invoke a route callable.
*
* #param callable $callable The callable to invoke using the strategy.
* #param ServerRequestInterface $request The request object.
* #param ResponseInterface $response The response object.
* #param array $routeArguments The route's placholder arguments
*
* #return ResponseInterface|string The response from the callable.
*/
public function __invoke( callable $callable, ServerRequestInterface $request, ResponseInterface $response, array $routeArguments)
{
$routeArguments['test'] = 'testing testing 123';
return parent::__invoke( $callable, $request, $response, $routeArguments );
}
}
My container declaration looks like this:
<?php
use Slim\App;
return function (App $app) {
$container = $app->getContainer();
$container['foundHandler'] = function() {
return new MyProject\Handlers\Strategies\SomeNewInvocationStrategy();
};
}
Then in all of my controller actions I have access to $args['test']. Further, this can be passed straight through to any Twig views.
This is useful for tasks like access control where by I always want to load the roles of users before processing the request but I'm sure there'll be many other use-cases for it.
I Hope this helps somebody.
This is my original code:
$app->post('/api/user', function (Request $request, Response $response, array $args) use ($tokenAuth) {
$tokenAuth->parseHeaders(); // validate token
//...do stuff with $tokenAuth data
$response->getBody()->write(json_encode(...)));
return $response;
});
Notice the use ($tokenAuth) this allows me to use this object within the method.
How can I use this method below in the same manner?
$app->post('/api/user', \UserController::class . ':add');
which works fine when the route hits in the class UserController:
class UserController {
public function add($request, $response, $args) { ...}
}
How can I pass $tokenAuth to this?
You can register $tokenAuth and UserController to dependency manager and pass $tokenAuth to UserController via dependency injection merchanism. For example:
<?php
namespace Your\Own\NameSpace;
use Your\Own\NameSpace\TokenAuth;
class UserController
{
private $tokenAuth;
public function __construct(TokenAuth $tokenAuth)
{
$this->tokenAuth = $tokenAuth;
}
public function add($request, $response, $args)
{
$this->tokenAuth->parseHeaders();
...
}
}
In your dependency registration code (assuming using Pimple):
<?php
use Your\Own\NameSpace\TokenAuth;
use Your\Own\NameSpace\UserController;
$container[TokenAuth::class] = function ($c) {
return new TokenAuth();
};
$container[UserController::class] = function ($c) {
$tokenAuth = $c->get(TokenAuth::class);
return new UserController($tokenAuth);
};
I ended up using middleware to authenticate on the route:
$mw = function($request, $response, $next){
$tokenAuth = new \TokenAuth();
$tokenAuth->parseHeaders();
$response = $next($request, $response);
return $response;
};
$app = new \Slim\App();
$app->post('/api/user', \UserController::class . ':add')->add($mw);
$app->run();
Take a look at your original code, you can use $tokenAuth (and it works), it means that you defined it globally before.
So, in your add method, just declare it as a global variable: global $tokenAuth; then use it;
public function add($request, $response, $args) {
global $tokenAuth;
$tokenAuth->parseHeaders();
...
}
I have created a simple aplication in Silex 1.3.4 and I want to have a base controller that will have a __construct method accepting $app and $request. All inheriting controllers then should have their respective constructors and calling the parent controller construct method.
//Use statements here....
class AppController
{
public function __construct(Application $app, Request $request){
$this->app = $app;
$this->request = $request;
}
}
Inheriting controllers would be written as below:
//Use statements here....
class ItemsController extends AppController
{
public function __construct(Application $app, Request $request){
parent::__construct($app, $request);
}
public function listAction()
{
//code here without having to pass the application and request objects
}
}
The approach I have decided on routing is as shown below:
$app->post(
'/items/list', 'MySilexTestDrive\Controller\ItemsController::listAction'
)->bind('list');
I was thinking of using the dispatcher and override some processes there and create my controller instances my own way but I do not have any idea how and if this is a great idea at all.
Anyone who has done something similar to this? Please help.
You can use ServiceControllerServiceProvider to define your controller as a service in the application. But you can't inject a Request in that way. BTW you can have more than one request and the request instance can change if you do sub-request. You can inject RequestStack instead, then call $requestStack->getCurrentRequest() when you need to get the current request.
$app = new Silex\Application();
abstract class AppController
{
protected $app;
protected $requestStack;
public function __construct(Silex\Application $app, Symfony\Component\HttpFoundation\RequestStack $requestStack)
{
$this->app = $app;
$this->requestStack = $requestStack;
}
public function getRequest()
{
return $this->requestStack->getCurrentRequest();
}
}
class ItemsController extends AppController
{
public function listAction()
{
$request = $this->getRequest();
// ...
}
}
$app->register(new Silex\Provider\ServiceControllerServiceProvider());
$app['items.controller'] = $app->share(function() use ($app) {
return new ItemsController($app, $app['request_stack']);
});
$app->get('/items/list', "items.controller:listAction");
It makes sense to do such a thing? I do not think so. Especially if the framework gives you a request instance thanks to the type hinting. Just do
public function listAction(Application $app, Request $request)
{
// ...
}
and work with that.
You can try this too :
class BaseController
{
protected $app;
protected $request;
public function __call($name, $arguments)
{
$this->app = $arguments[0];
$this->request = $arguments[1];
return call_user_func_array(array($this,$name), [$arguments[0], $arguments[1]]);
}
protected function getSystemStatus(Application $app, Request $request)
{
[...]
}
[...]
}
#Rabbis and #Federico I have come up with a more elegant solution for this where I have created a BeforeControllerExecuteListener that I dispatch with my application instance. This listener accepts the FilterControllerEvent object and then from my base controller I call a method where I inject both the Silex Application and the request from the event.
public function onKernelController(FilterControllerEvent $event)
{
$collection = $event->getController();
$controller = $collection[0];
if($controller instanceof BaseControllerAwareInterface){
$controller->initialize($this->app, $event->getRequest());
}
}
The I simple dispatch this in my bootstrap file as shown below:
$app['dispatcher']->addSubscriber(new BeforeControllerExecuteListener($app));
This allows me to have access to this object without having to add them as parameters on my actions. Below is how one of my actions in the making looks:
public function listAction($customer)
{
$connection = $this->getApplication()['dbs']['db_orders'];
$orders= $connection->fetchAll($sqlQuery);
$results = array();
foreach($orders as $order){
$results[$order['id']] = $order['number'] . ' (' . $order['customer'] . ')';
}
return new JsonResponse($results);
}
If the currently running controller being called honors the BaseControllerAwareInterface interface as I have defined it then it means I should inject that controller with the Application and Request instances. I leave the controllers to deal with how they manage the Response of each action as with my example above I may need the Response object itself of JsonResponse even any other type of response so it entirely depends on the controller to take care of that.
Then the routing remains as simply as:
$app->match('/orders/list/{cusstomer}', 'Luyanda\Controller\OrdersController::listAction')
->bind('list-orders');