I have simple Slim app that should render a html page.
This is my index.php
require __DIR__ . '/../vendor/autoload.php';
$app = new \RKA\Slim(
[
'mode' => 'development',
]
);
// Optionally register a controller with the container
$app->container->singleton('App\Home', function ($container) {
return new \App\Controller\Home();
});
// Set up routes
$app->get('/','App\Home:index');
This is my controller Home.php
namespace App\Controller;
class Home
{
protected $request;
protected $response;
public function index($app)
{
$this->app = $app;
$this->app->render('../test.html');
}
I get Message: Missing argument 1 for App\Controller\Home::index()
I see you're not using original Slim, but Slim extended using the RKA Slim Controller project.
As far as I understand the code, your index will not get the app passed as param. The method will get only URL params that the route defined (in this case none).
If you need reference to the app, implement method named setApp, that will be called automatically (when dispatching the route).
class Home
{
protected $request;
protected $response;
protected $app;
public function setApp($app)
{
$this->app = $app;
}
public function index() {
/* logic for the route
app is available as $this->app */
}
}
If you additionally implement setRequest and setResponse, you will get the request and response references.
Related
How can I set the store name and logo using the Omnipay Paypal package for Laravel?
Please see my code below, until now I tried to use the method setBrandName on the gateway variable, but this method was not found. It seems like the class ExpressCheckout has such methods but is like deprecated and one should use this code and checkout procedure. Also tried to add entry to the purchase method array parameter but it didn't work.
<?php
namespace App\Http\Controllers;
..
use Omnipay\Omnipay;
class PurchaseController extends Controller
{
public $gateway;
public function __construct()
{
$this->gateway = Omnipay::create('PayPal_Rest');
$this->gateway->setClientId(...);
$this->gateway->setSecret(...);
$this->gateway->setTestMode(true);
}
protected function makePurchase(Request $request) {
try {
$items = ...;
$response = $this->gateway->purchase(array(
'amount' => $cart->getSum(),
'items' => $items,
'currency' => config('paypal.currency'),
'returnUrl' => route('payment.success'),
'cancelUrl' => route('cart'),
))->send();
if ($response->isRedirect()) {
$response->redirect(); // this will automatically forward the customer
} else {
// not successful
return $response->getMessage() . ", " . $response->getCode();
}
} catch(Exception $e) {
return $e->getMessage();
}
}
protected function purchaseCompleted(Request $request) {
...
}
}
I need to set a custom attribute in the route definition and use it a route middleware. For example, I need to manage the refer page to redirect the user after the login.
This is my routes definition:
return function (App $app) {
$app->get('/', Home::class. ':home')->setName('home');
$app->get('/login', UserAction::class. ':getLogin')->setName('login')->setAttribute('norefer',true);
$app->post('/login', UserAction::class. ':postLogin');
};
The ->setAttribute('norefer',true); is what I'm looking for and seems it doesn't exist.
I need this attribute using ->getAttribute("norefer") in a middleware so I can store the last referable page visited by the user:
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$routeContext = RouteContext::fromRequest($request);
$route = $routeContext->getRoute();
if (!empty($route) && !$routeContext->getRoute()->getAttribute("norefer")) {
$referName = $routeContext->getRoute()->getName();
$referArgs = $routeContext->getRoute()->getArguments();
$this->session->set("referName", $referName);
$this->session->set("referArgs", $referArgs);
}
return $handler->handle($request);
}
So, in the session I can store the last referable page and use it after the login process to redirect the user to his page.
You could add a NoRefererMiddleware to routes you want to exclude from the redirection logic. NoRefererMiddleware just sets a noreferer attribute to the request object if its called.
<?php
use App\Middleware\NoRefererMiddleware;
use Slim\App;
return function (App $app) {
$app->get('/', Home::class. ':home')->setName('home');
$app->get('/login', UserAction::class. ':getLogin')->setName('login')->add(NoRefererMiddleware::class);
$app->post('/login', UserAction::class. ':postLogin');
};
File: src/Middleware/NoRefererMiddleware.php
<?php
namespace App\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
final class NoRefererMiddleware implements MiddlewareInterface
{
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$request = $request->withAttribute('noreferer', true);
return $handler->handle($request);
}
}
Usage
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$noReferer = $request->getAttribute('noreferer');
if ($noReferer !== true) {
$routeContext = RouteContext::fromRequest($request);
$route = $routeContext->getRoute();
if ($route !== null) {
$referName = $routeContext->getRoute()->getName();
$referArgs = $routeContext->getRoute()->getArguments();
$this->session->set('referName', $referName);
$this->session->set('referArgs', $referArgs);
}
}
return $handler->handle($request);
}
I've created my own service class and I have a function inside it, handleRedirect() that's supposed to perform some minimal logical check before choosing to which route to redirect.
class LoginService
{
private $CartTable;
private $SessionCustomer;
private $Customer;
public function __construct(Container $SessionCustomer, CartTable $CartTable, Customer $Customer)
{
$this->SessionCustomer = $SessionCustomer;
$this->CartTable = $CartTable;
$this->Customer = $Customer;
$this->prepareSession();
$this->setCartOwner();
$this->handleRedirect();
}
public function prepareSession()
{
// Store user's first name
$this->SessionCustomer->offsetSet('first_name', $this->Customer->first_name);
// Store user id
$this->SessionCustomer->offsetSet('customer_id', $this->Customer->customer_id);
}
public function handleRedirect()
{
// If redirected to log in, or if previous page visited before logging in is cart page:
// Redirect to shipping_info
// Else
// Redirect to /
}
public function setCartOwner()
{
// GET USER ID FROM SESSION
$customer_id = $this->SessionCustomer->offsetGet('customer_id');
// GET CART ID FROM SESSION
$cart_id = $this->SessionCustomer->offsetGet('cart_id');
// UPDATE
$this->CartTable->updateCartCustomerId($customer_id, $cart_id);
}
}
This service is invoked in the controller after a successful login or registration. I'm not sure what's the best way to access redirect()->toRoute(); from here (or if I should do it here).
Also if you have other comments on how my code is structured please feel free to leave them.
Using plugins within your services is a bad idea as they require a controller to be set. When a service is created and you inject a plugin it has no idea of the controller instance so it will result in an error exception. If you want to redirect the user you might just edit the response object as the redirect plugin does.
Notice that I stripped the code to keep the example clear and simple.
class LoginServiceFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
return new LoginService($container->get('Application')->getMvcEvent());
}
}
class LoginService
{
/**
* #var \Zend\Mvc\MvcEvent
*/
private $event;
/**
* RedirectService constructor.
* #param \Zend\Mvc\MvcEvent $event
*/
public function __construct(\Zend\Mvc\MvcEvent $event)
{
$this->event = $event;
}
/**
* #return Response|\Zend\Stdlib\ResponseInterface
*/
public function handleRedirect()
{
// conditions check
if (true) {
$url = $this->event->getRouter()->assemble([], ['name' => 'home']);
} else {
$url = $this->event->getRouter()->assemble([], ['name' => 'cart/shipping-info']);
}
/** #var \Zend\Http\Response $response */
$response = $this->event->getResponse();
$response->getHeaders()->addHeaderLine('Location', $url);
$response->setStatusCode(302);
return $response;
}
}
Now from within your controller you can do the following:
return $loginService->handleRedirect();
I had this working on a local dev environment, but now that I'm pushing it live I'm running into an error:
When I try to access my CRUD pages (/admin/images or similar), I get taken to my websites 404 page.
I uploaded the /routes/admin.php file, all my resource files, controllers, models, vendor files, public/vendor files, and probably some others I'm forgetting to mention.
Not sure if theres something in the config files for backpack I need to edit or what. Looking for some direction.
Note: I am able to access the default routes from Backpack (dashboard, login, logout)
RouteServiceProvider.php
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
protected function mapApiRoutes()
{
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}
protected function mapAdminRoutes()
{
Route::middleware(['web', 'admin'])
->prefix('admin') // or use the prefix from CRUD config
->namespace($this->namespace.'\Admin')
->group(base_path('routes/admin.php'));
}
Found this error in the error logs:
exception 'Illuminate\Database\Eloquent\RelationNotFoundException'
with message 'Call to undefined relationship [wheels] on model
[App\Models\WheelFinishes].' in
laravel/framework/src/Illuminate/Database/Eloquent/RelationNotFoundException.php:20
But I have the relationship defined in my WheelFinishes model
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Backpack\CRUD\CrudTrait;
class WheelFinishes extends Model
{
use CrudTrait;
public function wheels()
{
return $this->belongsTo('App\Models\Wheels', 'wheel_id');
}
...
}
Wheels Model
namespace App\Models;
use Laravel\Scout\Searchable;
use Illuminate\Database\Eloquent\Model;
use Backpack\CRUD\CrudTrait;
use App\User;
class Wheels extends Model
{
use CrudTrait;
protected $table = "wheels";
protected $primaryKey = 'id';
...
public function tips()
{
return $this->hasMany('App\Models\WheelTips', 'wheel_id');
}
public function finishes()
{
return $this->hasMany('App\Models\WheelFinishes', 'wheel_id')->where('status', '=', '1')->orderBy('order');
}
public function factoryFinishes()
{
return $this->hasMany('App\Models\WheelFinishes', 'wheel_id')->where('status', '=', '1')->where('factory_finish', '=', '1')->orderBy('order');
}
public function wheelImages()
{
return $this->hasMany('App\Models\WheelImages', 'wheel_id');
}
public function wheelImage()
{
return $this->hasOne('App\Models\WheelFinishes', 'wheel_id')->where('status', '=', '1')->orderBy('order');
}
public function profile()
{
return $this->BelongsTo('App\Models\Profile');
}
public function series()
{
return $this->BelongsTo('App\Models\Series');
}
public function vehicles()
{
return $this->hasMany('App\Models\Vehicles', 'wheel_id')->where('status', '=', '1')->orderBy('order')->take(3);
}
public function vehicle()
{
return $this->hasOne('App\Models\Vehicles', 'wheel_id')->where('status', '=', '1')->orderBy('order');
}
}
routes.php
<?php
// Backpack\CRUD: Define the resources for the entities you want to CRUD.
CRUD::resource('video', 'VideoCrudController');
CRUD::resource('wheels', 'WheelCrudController');
Route::get('finishes/ajax-finishes-options', 'FinishCrudController#wheelsOptions');
CRUD::resource('finishes', 'FinishCrudController');
Route::get('albums/ajax-albums-options', 'AlbumCrudController#albumsOptions');
CRUD::resource('albums', 'AlbumCrudController');
CRUD::resource('heros', 'HeroCrudController');
If you have a separate route file you probably need to register it in RouteServiceProvider:
Route::group([
'middleware' => 'web',
'namespace' => $this->namespace,
], function ($router) {
require base_path('routes/web.php');
require base_path('routes/admin.php');
});
How do I have a method, defined once, available in all controllers?
In my UsersController app I have a method called getAuthService (which fetches the authentication service), but I want to be able to access the authentication instance from other controller too (so I can access it's storage). Below is my method in UsersController:
class UsersController {
protected $authService
.
.
.
protected function getAuthService() {
if (! $this->authService) {
$dbAdapter = $this->getServiceLocator()->get('\Zend\Db\Adapter\Adapter');
$dbTableAuthAdapter = new DbTableAuthAdapter($dbAdapter, 'users', 'email', 'password', 'MD5(?)');
$authService = new AuthenticationService();
$authService->setAdapter($dbTableAuthAdapter);
$this->authService = $authService;
}
return $this->authService;
}
}
.. however, I cannot access this in my ApplicationController unless I copy the method in there? Can I define this method somewhere else? Or, another way?
In Rails I'd put this method into the application controller as other controllers extend from that. Is it the Zend way to create a controller containing shared methods that extends AbstractActionController and other controllers extend from that, or extend other modules' controllers from the Application\Controller\IndexController:
- Zend\Mvc\Controller\AbstractActionController (abstract)
- Application\Controller\IndexController (containing my getAuthService method)
- Users\Controller\UsersController (extends the above so getAuthService is available)
Thanks
You could write a Controller Plugin. Since you require the authservice aswell as the dbadapter it probably is a good idea to write a factory which retrieves those. Within your application/src/Controller we add a folder called Plugin. Once that is done we create our factory which fetches the required services etc.
namespace Application\Controller\Plugin;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
/**
*
* Your factory
*
* #package Application
*/
class AuthFactory implements FactoryInterface
{
/**
* Create Service Factory
*
* #param ServiceLocatorInterface $serviceLocator
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$sm = $serviceLocator->getServiceLocator();
$adapter = $sm->get('\Zend\Db\Adapter\Adapter');
$plugin = new Auth();
$plugin->setAdapter($adapter);
return $plugin;
}
}
The Plugin could be something like following:
namespace Application\Controller\Plugin;
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
class Auth extends AbstractPlugin
{
protected $adapter;
protected $authService;
public function setAdapter($adapter)
{
$this->adapter = $adapter;
}
public function getAdapter()
{
return $this->adapter;
}
public function getService()
{
if (! $this->authService) {
$dbAdapter = $this->getAdapter();
$dbTableAuthAdapter = new DbTableAuthAdapter($dbAdapter, 'users', 'email', 'password', 'MD5(?)');
$authService = new AuthenticationService();
$authService->setAdapter($dbTableAuthAdapter);
$this->authService = $authService;
}
return $this->authService;
}
}
Now we have to add our factory to the module.config file like so:
'controller_plugins' => array(
'factories' => array(
'auth' => 'Application\Controller\Plugin\AuthFactory',
),
),
Once that is done you can just call your controller plugin within the controller like so:
$this->auth()->getService();
//or the alternative syntax
$this->plugin('auth')->getService();