I registering a controller with the container, but it seems not working because it doesn't match to the correct location.
\slim\src\routes.php
<?php
// Routes
$app->get('/dd', 'App\controllers\HomeController:home');
\slim\App\controllers\HomeController.php
<?php
class HomeController
{
protected $container;
// constructor receives container instance
public function __construct(ContainerInterface $container) {
$this->container = $container;
}
public function home($request, $response, $args) {
// your code
// to access items in the container... $this->container->get('');
return $response;
}
public function contact($request, $response, $args) {
// your code
// to access items in the container... $this->container->get('');
return $response;
}
}
My project folder structure:
\slim
\public
index.php
.htaccess
\App
\controllers
HomeController.php
\src
dependencies.php
middleware.php
routes.php
settings.php
\templates
index.phtml
\vendor
\slim
Maybe I should to setting \slim\src\settings.php?
Because it show Slim Application Error:
Type: RuntimeException Message: Callable
App\controllers\HomeController does not exist File:
D:\htdocs\slim\vendor\slim\slim\Slim\CallableResolver.php Line: 90
Last, I also refer to these articles:
https://www.slimframework.com/docs/objects/router.html#container-resolution
PHP Slim Framework Create Controller
PHP Slim Framework Create Controller
How can i create middleware on Slim Framework 3?
How can i create middleware on Slim Framework 3?
Add psr-4 to your composer file so that you're able to call your namespaces.
{
"require": {
"slim/slim": "^3.12
},
"autoload": {
"psr-4": {
"App\\": "app"
}
}
}
This PSR describes a specification for autoloading classes from file paths. Then in your routes.php file add this at the top :
<?php
use app\controllers\HomeController;
// Routes
$app->get('/dd', 'App\controllers\HomeController:home');
and finally in your HomeController.php file add :
<?php
namespace app\controllers;
class HomeController
{
//.. your code
}
hope this helps...:)
Related
I'm trying to switch from the pimple container that comes bundled with Slim, to PHP-DI and I'm having an issue with getting the autowiring to work. As I'm restricted to using PHP 5.6, I'm using Slim 3.9.0 and PHP-DI 5.2.0 along with php-di/slim-bridge 1.1.
My project structure follows along the lines of:
api
- src
| - Controller
| | - TestController.php
| - Service
| - Model
| - ...
- vendor
- composer.json
In api/composer.json I have the following, and ran composer dumpautoload:
{
"require": {
"slim/slim": "3.*",
"php-di/slim-bridge": "^1.1"
},
"autoload": {
"psr-4": {
"MyAPI\\": "src/"
}
}
}
My api/src/Controller/TestController.php file contains a single class:
<?php
namespace MyAPI\Controller;
class TestController
{
public function __construct()
{
}
public function test($request,$response)
{
return $response->write("Controller is working");
}
}
I initially tried to use a minimal setup to get the autowiring working, just using the default configuration. index.php
<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
require '/../../api/vendor/autoload.php';
$app = new \DI\Bridge\Slim\App;
$app->get('/', TestController::class, ':test');
$app->run();
However, this returned the error:
Type: Invoker\Exception\NotCallableException
Message: 'TestController'is neither a callable nor a valid container entry
The only two ways I could get it to work, is to place the TestController class in index.php directly (which makes me think PHP-DI isn't playing well with the autoloader) or to use the following extension of \DI\Bridge\Slim\App. However, as I need to explicitly register the controller class, this kinda defeats the point of using autowiring (unless I'm missing the point):
use DI\ContainerBuilder;
use Psr\Container\ContainerInterface;
use function DI\factory;
class MyApp extends \DI\Bridge\Slim\App
{
public function __construct() {
$containerBuilder = new ContainerBuilder;
$this->configureContainer($containerBuilder);
$container = $containerBuilder->build();
parent::__construct($container);
}
protected function configureContainer(ContainerBuilder $builder)
{
$definitions = [
'TestController' => DI\factory(function (ContainerInterface $c) {
return new MyAPI\Controller\TestController();
})
];
$builder->addDefinitions($definitions);
}
}
$app = new MyApp();
$app->get('/', ['TestController', 'test']);
$app->run();
If you want to call the test method the syntax is :
$app->get('/', TestController::class .':test');
// or
$app->get('/', 'TestController:test');
rather than
$app->get('/', TestController::class ,':test');
cf https://www.slimframework.com/docs/v3/objects/router.html#container-resolution
I am currently trying to develop a custom Laravel Package, but is having some issues with getting started.
This is my file structure:
-packages
- oliverbusk
-invoiceconverter
-src
-controllers
- InvoiceconverterController.php
-resources
- views
- home.blade.php
-routes
- web.php
- InvoiceConverterServiceProvider.php
So as you can see, I have my files inside the src/ folder.
First of all, this is my composer.json, inside my package folder:
"extra": {
"laravel": {
"providers": [
"Oliverbusk\\Invoiceconverter\\InvoiceConverterServiceProvider"
]
}
}
I have then autoloaded this in my projects main composer.json file:
"require": {
//....
"oliverbusk/invoiceconverter": "dev-feature-package"
},
"autoload": {
[...]
"psr-4": {
"App\\": "app/",
"Oliverbusk\\Invoiceconverter\\": "packages/oliverbusk/invoiceconverter"
}
},
This is my serviceprovider file:
namespace Oliverbusk\Invoiceconverter;
use Illuminate\Support\ServiceProvider;
class InvoiceConverterServiceProvider extends ServiceProvider
{
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
//Load our routes
$this->loadRoutesFrom(__DIR__ . '/routes/web.php');
//Load our views
$this->loadViewsFrom(__DIR__ . '/resources/views', 'invoiceconverter');
}
/**
* Register services.
*
* #return void
*/
public function register()
{
//
}
}
And my controller file, located in controllers/:
namespace Oliverbusk\Invoiceconverter\Controllers;
use App\Http\Controllers\Controller;
class InvoiceconverterController extends Controller
{
public function index()
{
return view('invoiceconverter::home');
}
}
Last, my routes/web.php file:
Route::group(['namespace' => 'Oliverbusk\InvoiceConverter\Controllers'], function () {
Route::get('invoiceconverter', 'InvoiceconverterController#index');
});
Error :
Class Oliverbusk\InvoiceConverter\Controllers\InvoiceconverterController does not exist
Bonus info:
Composer dump autoload shows the following:
Discovered Package: oliverbusk/invoiceconverter
I have already tried to clear the cache with php artisan:cache:clear.
I have also tried composer update
The namespace in your route does not match the namespacing your have actually used.
Route::group(['namespace' => 'Oliverbusk\InvoiceConverter\Controllers'], function () {
Route::get('invoiceconverter', 'InvoiceconverterController#index');
});
Change to
Route::group(['namespace' => 'Oliverbusk\Invoiceconverter\Controllers'], function ()
{
Route::get('invoiceconverter', 'InvoiceconverterController#index');
});
And see if that helps.
I would also recommend you refactor all your code to be capitalised InvoiceConverter as they are two separate words.
I read the documentation here about creating middleware. But which folder or file i must be create it? Documentation is not contain this information.
Under the src folder i have middleware.php.
For example i want to get post information like this:
$app->post('/search/{keywords}', function ($request, $response, $args) {
$data = $request->getParsedBody();
//Here is some codes connecting db etc...
return json_encode($query_response);
});
i made this under the routes.php but i want to create class or middleware for this. How can i do? Which folder or file i must be use.
Slim3 does not tie you to a particular folder structure, but it does (rather) assume you use composer and use one of the PSR folder structures.
Personally, that's what I use (well, a simplified version):
in my index file /www/index.php:
include_once '../vendor/autoload.php';
$app = new \My\Slim\Application(include '../DI/services.php', '../config/slim-routes.php');
$app->run();
In /src/My/Slim/Application.php:
class Application extends \Slim\App
{
function __construct($container, $routePath)
{
parent::__construct($container);
include $routePath;
$this->add(new ExampleMiddleWareToBeUsedGlobally());
}
}
I define all the dependency injections in DI/services.php and all the route definitions in config/slim-routes.php. Note that since I include the routes inside the Application constructor, they will have $this refer to the application inside the include file.
Then in DI/services.php you can have something like
$container = new \Slim\Container();
$container['HomeController'] = function ($container) {
return new \My\Slim\Controller\HomeController();
};
return $container;
in config/slim-routes.php something like
$this->get('/', 'HomeController:showHome'); //note the use of $this here, it refers to the Application class as stated above
and finally your controller /src/My/Slim/Controller/HomeController.php
class HomeController extends \My\Slim\Controller\AbstractController
{
function showHome(ServerRequestInterface $request, ResponseInterface $response)
{
return $response->getBody()->write('hello world');
}
}
Also, the best way to return json is with return $response->withJson($toReturn)
I used the following tutorial to get an idea about interfaces:
http://vegibit.com/what-is-a-laravel-interface/
But I wanted to change the directory of where I am putting my interfaces to "App/Models/Interfaces". And so I did. But now I cannot get it to work anymore. Here is my code:
Routes.php
App::bind('CarInterface', 'Subaru');
Route::get('subaru', function()
{
$car = App::make('CarInterface');
$car->start();
$car->gas();
$car->brake();
});
Model Subaru.php
<?php
use App\Models\Interfaces\CarInterface;
class Subaru implements CarInterface {
..etc
Interface CarInterface
<?php namespace App\Models\Interfaces;
interface CarInterface {
public function start();
public function gas();
public function brake();
}
I added this in my composer.json:
"psr-0": {
"Interfaces": "app/models/interfaces"
}
And I even added this in my start/global.php file:
ClassLoader::addDirectories(array(
app_path().'/models/interfaces',
In my recent laravel 5 project, I'm used to prepare my logics as Repository method.
So here's my current directory structure. For example we have 'Car'.
So first I just create directory call it libs under app directory and loaded it to composer.json
"autoload": {
"classmap": [
"database",
"app/libs" //this is the new changes (remove this comment)
]
}
after that I create a subfolder call it Car . Under the Car folder create two file 'CarEloquent.php' for eloquent implementation and CarInterface.php as interface.
CarInterface
namespace App\libs\Car;
interface CarInterface {
public function getAll();
public function create(array $data);
public function delete($id);
public function getByID($id);
public function update($id,array $data);
}
CarEloquent
namespace App\lib\Car;
use App\lib\Car\CarInterface;
use App\Car; //car model
class CarEloquent implements CarInterface {
protected $car;
function __construct(Car $a) {
$this->car = $a;
}
public function getAll(){
return $this->car->all();
}
}
Then create Car Service Provider to bind ioc controller.
For create Car service provider you can also use php artisan command by laravel.
php artisan make:provider CarServiceProvider
ServiceProvider
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class CarServiceProvider extends ServiceProvider {
public function register() {
$this->app->bind('App\lib\Car\CarInterface', 'App\lib\Car\CarEloquent');
}
}
And final step would be add these service provider to config/app.php provider array.
'providers' => [
'App\Providers\CatServiceProvider',
]
And finally we are ready to use our repository method in our controller.
Example Controller
namespace App\Http\Controllers;
use App\lib\Car\CarInterface as Car;
class CarController extends Controller {
protected $carObject;
public function __construct(Car $c) {
$this->carObject = $c;
}
public function getIndex(){
$cars = $this->carObject->getAll();
return view('cars.index')->with('cars',$cars);
}
}
Main purpose to achieve here call repository method to controller, however you need use them as per your requirement.
Update
CarEloqent basically help us to improve database implementation, for example in future if you want to implement same functionality for other database like redis you just add another class CarRedis and change implementation file path from server provider.
Update 1: Good Resource
http://programmingarehard.com/2014/03/12/what-to-return-from-repositories.html
[book] From Apprentice to Artisan by Taylor Otwell
Very good explanation about repository method and software design principle commonly called separation of concerns. You should read this book.
If you still have any confusion to achieve these behaviors let me know and however I will keep eye on this question to update this answer, if I find some things to change or update or as per requirement.
In using the laravel framework, how can I call a function defined in base_controller, in a view. For exacmple:
class Base_Controller extends Controller {
public static function format_something()
{
return something;
}
}
How can i call format_something() in a view file?
Usually the error I get looks something like this:
Method [link_to_action] is not defined on the View class.
Probably a silly question, but thanks in advance!
Edit
Okay! First the correct place to do something like this is in the libraries folder.
Second, problem is that your class cannot have underscores.
So in application/libraries I made file AppHelper.php with class
class AppHelper {
public static function format_something()
{
return something;
}
}
And can call it like:
$formated = AppHelper::format_something;
Thanks for the help and the good forum find Boofus McGoofus.
For me is working:
Create directory "helpers" or whatever and file:
// app/helpers/AppHelper.php
class AppHelper {
public static function format_something()
{
return something;
}
}
Add path to composer.json
// composer.json
"autoload": {
"classmap": [
"app/helpers" // <-------- add this line
]
},
Run: (reload the autoload)
composer dump-autoload
Now you can call:
$formated = AppHelper::format_something();
This answer was written for Laravel 3. For Laravel 4 and after, Lajdák Marek's answer using Composer's autoloader is better.
Functions like format_something() don't belong in the controller. The controller should just be about collecting data from various sources and passing it to the view. It's job is mostly just routing.
I've created a folder called "helpers" in the application folder for all my little helpery functions. To make sure all my controllers, views, and models have access to them, I've included the following in my start.php file:
foreach(glob(path('app').'helpers/*.php') as $filename) {
include $filename;
}
I suspect that there's a better way to do that, but so far it has worked for me.
You can inspire yourself from Laravel framework itself.
I will take your example of a formatter and refer to url helper in Laravel Framework.
Start by creating your own helpers.php file:
<?php
if (! function_exists('format_that')) {
/**
* Generate something
*
* #param string $text
* #return string
*/
function format_that($text)
{
return app('formatter')->format_that($text);
}
}
And add it to your composer.json file:
"autoload": {
"files": [
"app/helpers/helpers.php"
]
}
Run this command to recreate the autoload php file:
$ composer dumpautoload
Create your service provider app/Providers/FormatterServiceProvider.php:
<?php
namespace Illuminate\Routing;
use Illuminate\Support\ServiceProvider;
use App\Helpers\FormatGenerator;
class FormatterServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->app['formatter'] = $this->app->share(function ($app) {
return new FormatGenerator($app['request']);
});
}
}
Register your service provider. Laravel framework invokes register method but you only need to add it to your app config file config/app.php:
'providers' => [
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
// other providers...
App\Providers\FormatterServiceProvider::class,
]
Finally, create your actual generator class app/Helpers/FormatGenerator.php
<?php
namespace App\Helpers;
use Illuminate\Http\Request;
class FormatGenerator
{
protected $request;
/**
* Create a new URL Generator instance.
*
* #param \Illuminate\Routing\RouteCollection $routes
* #param \Illuminate\Http\Request $request
* #return void
*/
public function __construct(Request $request)
{
$this->request = $request;
}
public function format_that($text){
if ($request->path() == "home"){
return mb_strtoupper($text);
}
else{
return $text;
}
}
}
You can optionally create a Facade app/Facade/Formatter.php, to be able to do Formatter::format_that($text):
<?php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
/**
* #see \App\Helpers\FormatGenerator
*/
class Formatter extends Facade
{
protected static function getFacadeAccessor() { return 'formatter'; }
}
You could ask yourself:
Why the facade? You can reuse the component somewhere else by simply calling Formatter::format_that($text) instead of app('formatter')->format_that($text). Sugar syntax really.
Why the Service provider? Dependence injections. If you need to use Request or want to build a complex object, the Service provider will take care of that for you and make it available in your $app object.