I’m moving a project of mine into Slim Microframework, by using PHP-DI containerization.
I need to POST some JSON data to http://localhost/api/base (XAMPP) and get a confirmation (at least at the beginning).
This is my index.php:
<?php
use PsrHttpMessageResponseInterface as Response;
use PsrHttpMessageServerRequestInterface as Request;
use SlimFactoryAppFactory;
use SlimExceptionHttpMethodNotAllowedException;
use SlimExceptionHttpNotFoundException;
use SlimRoutingRouteContext;
use DIContainerBuilder;
// Set error reporting to display all errors
error_reporting(E_ALL);
ini_set('display_errors', 'On');
// Autoloading using Composer's autoload
require __DIR__ . '/../vendor/autoload.php';
// Include dependencies file
$container = require_once __DIR__ . '/../config/dependencies.php';
// Create Slim app
$app = AppFactory::create();
// Include routes
require __DIR__ . '/../src/routes.php';
$app->run();
This is routes.php:
<?php
use PsrHttpMessageResponseInterface as Response;
use PsrHttpMessageServerRequestInterface as Request;
use AppControllersHotelBaseController;
// Include dependencies file
$container = require_once __DIR__ . '/../config/dependencies.php';
// Define basic route
$app->get('/', function (Request $request, Response $response) {
$response->getBody()->write('Hello, World!');
return $response;
});
// Route for handling POST request to /api/base
$app->post('/api/base', function (Request $request, Response $response) use ($container) {
// Get the HotelBaseController service from the container
$hotelBaseController = $container->get('HotelBaseController');
// Call the handlePostRequest method
return $hotelBaseController->handlePostRequest($request, $response);
});
$app->get('/api/base/', function (Request $request, Response $response) {
return $response->withStatus(301)->withHeader('Location', '/api/base');
});
This is the dependencies.php:
<?php
use DIContainerBuilder;
// use PsrHttpMessageResponseInterface as Response;
// use PsrHttpMessageServerRequestInterface as Request;
require __DIR__ . '/../vendor/autoload.php';
// Create ContainerBuilder instance
$containerBuilder = new ContainerBuilder();
// Set up your container configuration
$containerBuilder->addDefinitions([
'HotelBaseController' => DIcreate(AppControllersHotelBaseController::class),
]);
// Build the container
return $containerBuilder->build();
And, finally, this is hotelBaseController.php:
<?php
namespace AppControllers;
use PsrHttpMessageResponseInterface as Response;
use PsrHttpMessageServerRequestInterface as Request;
class HotelBaseController {
public function handlePostRequest(Request $request, Response $response) {
// Get the JSON data from the request body
$data = $request->getParsedBody();
// You can access the individual fields from the JSON data
$userId = $data['userId'] ?? '';
$apiKey = $data['apiKey'] ?? '';
$documentType = $data['document_type'] ?? '';
$hotelId = $data['hotel_id'] ?? '';
$hotelName = $data['name'] ?? '';
$res = 'Who knows';
return $res;
}
}
?>
When sending any POST request, containing a simple JSON, I get the error:
<b>Fatal error</b>: Uncaught Error: Call to a member function get() on bool in C:xampphtdocssrcroutes.php:20
Stack trace:
#0 C:xampphtdocsvendorslimslimSlimHandlersStrategiesRequestResponse.php(38):
{closure}(Object(SlimHttpServerRequest), Object(SlimHttpResponse), Array)
#1 C:xampphtdocsvendorslimslimSlimRoutingRoute.php(358):
SlimHandlersStrategiesRequestResponse->__invoke(Object(Closure), Object(SlimHttpServerRequest),
Object(SlimHttpResponse), Array)
#2 C:xampphtdocsvendorslimslimSlimMiddlewareDispatcher.php(65):
SlimRoutingRoute->handle(Object(SlimHttpServerRequest))
#3 C:xampphtdocsvendorslimslimSlimMiddlewareDispatcher.php(65):
SlimMiddlewareDispatcher->handle(Object(SlimHttpServerRequest))
#4 C:xampphtdocsvendorslimslimSlimRoutingRoute.php(315):
SlimMiddlewareDispatcher->handle(Object(SlimHttpServerRequest))
#5 C:xampphtdocsvendorslimslimSlimRoutingRouteRunner.php(68):
SlimRoutingRoute->run(Object(SlimHttpServerRequest))
#6 C:xampphtdocsvendorslimslimSlimMiddlewareDispatcher.php(65):
SlimRoutingRouteRunner->handle(Object(SlimHttpServerRequest))
#7 C:xampphtdocsvendorslimslimSlimApp.php(199):
SlimMiddlewareDispatcher->handle(Object(SlimHttpServerRequest))
#8 C:xampphtdocsvendorslimslimSlimApp.php(183): SlimApp->handle(Object(SlimHttpServerRequest))
#9 C:xampphtdocspublicindex.php(50): SlimApp->run()
#10 {main}
thrown in <b>C:xampphtdocssrcroutes.php</b> on line <b>20</b><br />
I would like to understand where am I making a mistake.
Thanks
2
Answers
The error message contains
bool
? You have$container = require_once
? I know exactly what’s going on.See,
require_once
acts differently thanrequire
. Not just in the fact that it only requires the code once, but also for scripts that return a value.The first time you require it, you’ll get your value as expected.
The second time however, you’ll only get
false
. That’s because since the file had already been loaded, it didn’t load it again.There’s no "global require_once return value cache" sadly.
What you’ll have to do is require it only once, and propagate that variable instead of loading it another time.
Vivick’s answer explains well what causes the error mentioned in question. But, it doesn’t explain how to setup Slim’s routes properly.
First, in your
dependencies.php
file you are importing the composer’s autoloader that is already imported inindex.php
. You don’t need to import it again so you should remove the linerequire __DIR__ . '/../vendor/autoload.php';
from there.Another thing there is that I would recommend using fully qualified class name instead of unqualified alias for the controller definition.
So your
dependencies.php
can look like this:I’ve took advantage of the fact that you can leave out the class name parameter in
DIcreate()
function call if it’s same as the name of definition.In your
index.php
you are not passing the container you’ve created toSlimFactoryAppFactory
so your container is created but it’s not used by Slim. Pass your container toAppFactory
before creating app instance like this:Now that the DI container is properly set up, you can use it in
routes.php
. When you define route as anonymous callback function the function is automatically bound to DI container, so you don’t need to pass it into function usinguse ($container)
.Your
routes.php
might look like this:What’s more, you can take advantage of Slim’s container resolution of routes to make it even more simple: