How to access to Slim container from other classess? - slim

Suppose that in my dependencies.php file I setup this container:
<?php
$container = $app->getContainer();
$container['db'] = function($config)
{
$db = $config['settings']['db'];
$pdo = new PDO("mysql:host=" . $db['host'] . ";port=" . $db['port'] .
";dbname=" . $db['dbname'] . ";charset=" . $db['charset'], $db['user'], $db['pass']);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
return $pdo;
};
I can use this container inside my route, called for example table.php:
<?php
use Slim\Http\Request;
use Slim\Http\Response;
$app->get('/table/get_table', function (Request $request, Response $response, array $args)
{
$sql = $this->db->prepare("SELECT * FROM some_table");
$sql->execute();
$result = $sql->fetchAll();
return $response->withJson($result);
});
this is basically the default usage, right? Said that, how can I instead use the db container from other classes? Supposes I have created a class called TableUtility and imported inside the table.php:
class TableUtility
{
function GetTableFromDb()
{
$sql = $this->db->prepare("SELECT * FROM some_table");
$sql->execute();
$result = $sql->fetchAll();
return $response->withJson($result);
}
}
as you can see I moved the logic of PDO inside GetTableFromDb of TableUtility, how can I access to db container from this class?
The usage in table.php will be:
<?php
use Slim\Http\Request;
use Slim\Http\Response;
$app->get('/table/get_table', function (Request $request, Response $response, array $args)
{
$tableUtility = new TableUtility();
return $response->withJson($tableUtility->GetTableFromDb());
});
actually I get in TableUtility:
Call to a member function prepare() on null

The full name for what you refer to as container is Dependency Injection Container. It is supposed to contain dependencies for objects. Passing this container to objects is considered bad practice. Instead you should pass only required dependencies for that object, which in your case is to pass db to $tableUtility. This is usually used by passing dependencies when constructing the object, or using setter methods. In your case you can refactor your code like this:
class TableUtility
{
function __construct($db) {
$this->db = $db;
}
}
Now in any method of TableUtility class, you have access to db object using $this->db but you'll need to pass db to class constructor whenever you create a new object. So you also need to do this:
$app->get('/table/get_table', function (Request $request, Response $response, array $args)
{
$tableUtility = new TableUtility($this->db);
// rest of the code
});

Related

Slim 4 get all routes into a controller without $app

I need to get all registed routes to work with into a controller.
In slim 3 it was possible to get the router with
$router = $container->get('router');
$routes = $router->getRoutes();
With $app it is easy $routes = $app->getRouteCollector()->getRoutes();
Any ideas?
If you use PHP-DI you could add a container definition and inject the object via constructor injection.
Example:
<?php
// config/container.php
use Slim\App;
use Slim\Factory\AppFactory;
use Slim\Interfaces\RouteCollectorInterface;
// ...
return [
App::class => function (ContainerInterface $container) {
AppFactory::setContainer($container);
return AppFactory::create();
},
RouteCollectorInterface::class => function (ContainerInterface $container) {
return $container->get(App::class)->getRouteCollector();
},
// ...
];
The action class:
<?php
namespace App\Action\Home;
use Psr\Http\Message\ResponseInterface;
use Slim\Http\Response;
use Slim\Http\ServerRequest;
use Slim\Interfaces\RouteCollectorInterface;
final class HomeAction
{
/**
* #var RouteCollectorInterface
*/
private $routeCollector;
public function __construct(RouteCollectorInterface $routeCollector)
{
$this->routeCollector = $routeCollector;
}
public function __invoke(ServerRequest $request, Response $response): ResponseInterface
{
$routes = $this->routeCollector->getRoutes();
// ...
}
}
This will display basic information about all routes in your app in SlimPHP 4:
$app->get('/tests/get-routes/', function ($request, $response, $args) use ($app) {
$routes = $app->getRouteCollector()->getRoutes();
foreach ($routes as $route) {
echo $route->getIdentifier() . " → ";
echo ($route->getName() ?? "(unnamed)") . " → ";
echo $route->getPattern();
echo "<br><br>";
}
return $response;
});
From there, one can use something like this to get the URL for a given route:
$routeParser = \Slim\Routing\RouteContext::fromRequest($request)->getRouteParser();
$path = $routeParser->urlFor($nameofroute, $data, $queryParams);
With the following caveats:
this will only work for named routes;
this will only work if the required route parameters are provided -- and there's no method to check whether a route takes mandatory or optional route parameters.
there's no method to get the URL for an unnamed route.

Slim Framework - Using a GET route

I'm absolutely new to Slim Framework. I'm working on an Webservice that should provide an interface between an Android App and a Web-Application. I used the Slim Documentation to make my first steps and now I want to create a simple GET route, to receive information from the App. Here is what I have so far:
require 'Slim/Slim.php';
\Slim\Slim::registerAutoloader();
$name_outside = '';
$app = new \Slim\Slim();
$app->get('/session/program_name/:name', function ($name) use($app) {
$name_outside = $name;
echo $name;
});
$app->run();
echo $name_outside;
I need to access the variable :name outside the function, but what I get is nothing. What I am doing wrong here?
Btw: I know that GET-routes usually are used to list existing resources, but for my simple case, I decided to use it that way.
Fix your code to hold name as args parameters ,then you can get in in Your function
require 'Slim/Slim.php';
\Slim\Slim::registerAutoloader();
$name_outside = '';
$app = new \Slim\Slim();
$app->get('/session/program_name/{name}', function ($args) use($app) {
$name_outside = $args['name'];
echo $args['name'];
});
$app->run();
To acess the $name_outside inside the function context you pass it.
$app->get('/session/program_name/:name', function ($name) use($app, &$name_outside) {
$name_outside = $name;
echo $name;
});
But perhaps you're using Slim in a wrong way. Why you have to access the variable outside of your route?
No code is execyted after the run() call. That's the way slim works, try to put a die where you're echo the variable, it isn't reachable.
You shouldn't have the need to access the context of the route outside of it this way. To transform a request or a response you use middlewares with the hooks.

zfcuser add user role after registration

I'm using Zend Framework 2 with ZfcUser, BjyAuthorize and Doctrine for the database. Registration etc. works very well so far. My problem is, that registered users have no role assigned, so i want to add the role "user" to the user during registration.
I think i could attach this to the "register" event, but i don't know how to do that.
I hope someone can help me ...
(i used this tutorial for setting up zfcuser etc. http://samminds.com/2013/03/zfcuser-bjyauthorize-and-doctrine-working-together/)
public function onBootstrap(MvcEvent $e)
{
$zfcServiceEvents = $e->getApplication()->getServiceManager()->get('zfcuser_user_service')->getEventManager();
$zfcServiceEvents->attach('register', function($e) {
$user = $e->getParam('user');
// probably the role must be added here, with $user->addRole();
// but how do i get the user Role Entity to add from DB?
});
Building on DangelZM's answer, and using another reference (see link at end of my post) about the Event Manager, I came up with this solution which organizes the potential ZfcUser event listeners out into a user listener object.
Note: I created my own user module called NvUser, so depending on the name of your module you'll have to replace all references of NvUser to your user module name.
Summary
I created an NvUserListener object that can itself attach event listeners to the shared event manager, and house the event listener callbacks.
Inside NvUser/Module.php:
<?php
namespace NvUser;
use Zend\Mvc\MvcEvent;
use NvUser\Listener\NvUserListener;
class Module
{
public function onBootstrap(MvcEvent $mvcEvent)
{
$em = $mvcEvent->getApplication()->getEventManager();
$em->attach(new NvUserListener());
}
}
Inside NvUser/src/NvUser/Listener/NvUserListener.php:
<?php
namespace NvUser\Listener;
use Zend\EventManager\AbstractListenerAggregate;
use Zend\EventManager\EventManagerInterface;
use Zend\EventManager\Event;
class NvUserListener extends AbstractListenerAggregate
{
public function attach(EventManagerInterface $events)
{
$sharedManager = $events->getSharedManager();
$this->listeners[] = $sharedManager->attach('ZfcUser\Service\User', 'register', array($this, 'onRegister'));
$this->listeners[] = $sharedManager->attach('ZfcUser\Service\User', 'register.post', array($this, 'onRegisterPost'));
}
public function onRegister(Event $e)
{
$sm = $e->getTarget()->getServiceManager();
$em = $sm->get('doctrine.entitymanager.orm_default');
$user = $e->getParam('user');
$config = $sm->get('config');
$criteria = array('roleId' => $config['zfcuser']['new_user_default_role']);
$defaultUserRole = $em->getRepository('NvUser\Entity\Role')->findOneBy($criteria);
if ($defaultUserRole !== null)
{
$user->addRole($defaultUserRole);
}
}
public function onRegisterPost(Event $e)
{
$user = $e->getParam('user');
$form = $e->getParam('form');
// Do something after user has registered
}
}
Inside NvUser/config/module.config.php:
<?php
namespace NvUser;
return array(
'zfcuser' => array(
'new_user_default_role' => 'user',
),
);
References:
Understanding the Zend Framework 2 Event Manager
Maybe it's not the best solution, but it works for me.
Add user_role_id option in config scope.
public function onBootstrap(MvcEvent $mvcEvent)
{
$zfcServiceEvents = $mvcEvent->getApplication()->getServiceManager()->get('zfcuser_user_service')->getEventManager();
$zfcServiceEvents->attach('register', function($e) use($mvcEvent) {
$user = $e->getParam('user');
$em = $mvcEvent->getApplication()->getServiceManager()->get('doctrine.entitymanager.orm_default');
$config = $mvcEvent->getApplication()->getServiceManager()->get('config');
$defaultUserRole = $em->getRepository('SamUser\Entity\Role')->find($config['user_role_id']);
$user->addRole($defaultUserRole);
});
}
Maybe someone will offer better solution.
This work too.
public function onBootstrap(MvcEvent $mvcEvent)
{
$zfcServiceEvents = $mvcEvent->getApplication()-getServiceManager()->get('zfcuser_user_service')->getEventManager();
$zfcServiceEvents->attach('register', function($e) use($mvcEvent) {
$user = $e->getParam('user');
$em = $mvcEvent->getApplication()->getServiceManager()-get('doctrine.entitymanager.orm_default');
$defaultUserRole = $em->getRepository('SamUser\Entity\Role')-find('id_of_your_role_on_table_role_for_example: '2'');
$user->addRole($defaultUserRole);
});
}
I just used a MySQL trigger
DROP TRIGGER IF EXISTS `user_role_after_insert_trig`;
DELIMITER //
CREATE TRIGGER `user_role_after_insert_trig` AFTER INSERT ON `user`
FOR EACH ROW begin
insert into user_role_linker (user_id,role_id) values (new.user_id, 5);
end
//
DELIMITER ;

zend, i cant call any view helpers

i have a got a helpers folder in my views folder with a helper called Log.php
/views/helpers/log.php
which contains:
class Zend_View_Helper_Log extends Zend_View_Helper_Abstract
{
public function loggedAs ()
{
$auth = Zend_Auth::getInstance();
if ($auth->hasIdentity()) {
$username = $auth->getIdentity()->uname;
$logoutUrl = $this->view->url(array('controller'=>'auth', 'action'=>'logout'), null, true);
return 'Hello' . $username . '. Logout?';
}
}
}
how can i call this from layouts? or views? i tried $this->_helpers->log->loggedAs();
but doesnt display anything, just an error:Fatal error: Call to a member function loggedAs() on a non-object in ...
I have a little experience in ZF. Yesterday I have the same problem and I decided its with the following code.
In the main Bootstrap.php I defined helper Path and Prefix
protected function _initDoctype()
{
$this->bootstrap('view');
$view = $this->getResource('view');
$view->doctype('XHTML1_STRICT');
$view->addHelperPath(APPLICATION_PATH . "/../library/My/Helper/View", "My_Helper_View");
}
After that in view file I used next syntax
$this->getPhoneString($value['per_telephone_number']);
where getPhoneString method in my Helper Class My_Helper_View_GetPhoneString
Hope this example will be useful for you :)
Your helper class should have a method that matches the name of the helper, and this is what you call. So if you want to call loggedAs() from your templates then this is what you should name your helper:
class Zend_View_Helper_LoggedAs extends Zend_View_Helper_Abstract
{
public function loggedAs()
{
$auth = Zend_Auth::getInstance();
if ($auth->hasIdentity()) {
$username = $auth->getIdentity()->uname;
$logoutUrl = $this->view->url(array('controller'=>'auth', 'action'=>'logout'), null, true);
return 'Hello' . $username . '. Logout?';
}
}
}
this should then live in a file at application/views/helpers/LoggedAs.php, and you'd call it from within your templates like this:
<?=$this->loggedAs()?>
I'd also recommend using your own namespace instead of Zend in the class name, but the way you've done it should work as well.

Zend Framework: Need advice on how to implement a controller helper

i need advice on how i can implement this action helper. currently, i have something like
class Application_Controller_Action_Helper_AppendParamsToUrl extends Zend_Controller_Action_Helper_Abstract {
function appendParamsToUrl($params = array()) {
$router = Zend_Controller_Front::getInstance()->getRouter();
$url = $router->assemble($params);
if (!empty($_SERVER['QUERY_STRING'])) {
$url .= $_SERVER['QUERY_STRING'];
}
return $url;
}
}
but as you can see, i think the function should be a static function? but how will that find into this Zend_Controller_Action_Helper thingy?
Make the function public and in your BootStrap.php ensure the controller helper can be autoloaded
// add paths to controller helpers
Zend_Controller_Action_HelperBroker::addPath( APPLICATION_PATH .'/controllers/helpers');
You should then be able to call the helper from your controller via
$this->_helper->appendParamsToUrl->appendParamsToUrl();
You can also rename appendParamsToUrl() function to direct()
function direct( $params = array() ) {...}
In this case, you'll be able to access it from controller with
$this->_helper->appendParamsToUrl( $params );