TYPO3 how to inject objectManager in hook? - dependency-injection

Maybe it's just simple, but I can't figure it out.
TYPO3 8.7: I am programming a small hook: if a certain condition is met I want to send an email. Therefore I need the standalone view for the email template. But for the standalone view I need the object manager:
/** #var \TYPO3\CMS\Fluid\View\StandaloneView $emailView
$emailView = $this->objectManager->get('TYPO3\\CMS\\Fluid\\View\\StandaloneView');
At the beginning of my class I tried to inject the objectManager:
/**
* #var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
*/
protected $objectManager;
/**
* #param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
* #internal
*/
public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
{
$this->objectManager = $objectManager;
}
But it does not work: I run into an error: the objectManager is a null object. Which obviously means that the injection mechanism is not present in the hook.
How can this be achieved then?

Extbase Dependency Injection is not available in hooks, so you have to create instances of objects yourself.
$standaloneView = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class)
->get(TYPO3\CMS\Fluid\View\StandaloneView::class);

Related

Can't inject repository on some controller Typo3

In my custom controller, i'm trying to inject a repository as a dependecy, like this
class FundedProjectController extends ActionController {
/**
* #var \GeorgRinger\News\Domain\Repository\NewsRepository
*/
protected $newsRepository;
/**
* Inject a news repository to enable DI
*
* #param \GeorgRinger\News\Domain\Repository\NewsRepository $newsRepository
*/
public function injectNewsRepository(\GeorgRinger\News\Domain\Repository\NewsRepository $newsRepository)
{
$this->newsRepository = $newsRepository;
}
...
But when I call it in my previewAction, i have an error saying that my $this->newsRepository is null.
But this injection is working on this controller, but I can't figure out why...
class NewsController extends NewsBaseController
I'm working on 7.6.32, stagging website but production mode enable, all caches cleared (also tried with ?no_cache=1, also tried with "#inject"
TYPO3 now requires to configure dependency injection. It does not work automatically any more. Basically you need to add a special Configuration/Services.yaml file. See here how to configure it.

Best Practise including code-completion in ZF2

In the following code the "/** #var BusinessLogic\User $user */" is not enabling code completion. When going by mouse over User in the comment I got:
"Multiple Declarations: this version of IDE will have problems with completion member resolution and inheritance anallysis for all classes that have multiple definitions in project files (regardles of includes)"
public function indexAction() {
/** #var BusinessLogic\User $user */
$user = $this->getServiceLocator()->get('userBusinessLogic');
$user->setUsername('testUsername');
}
I think Jetbrains is already working on it: http://youtrack.jetbrains.com/issue/WI-2760 and all related Tasks.
The only way I found to enable this is:
use BusinessLogic\User;
public function indexAction() {
/** #var User $user */
$user = $this->getServiceLocator()->get('userBusinessLogic');
$user->setUsername('testUsername');
}
But when I put:
use BusinessLogic\User;
into the code I can instantiate the user by
$user = new User();
without serviceLocator; not good for other developers to work on this file afterwards.
Some ideas? Code-Completion is quite important.
Try adding leading slash before namespace.
Your first attempt tells IDE to reference class relative to current namespace (i.e. if current namespace is \Website\Shop then FQN will be \Website\Shop\BusinessLogic\User).
With leading slash you will make it FQN. So ... /** #var \BusinessLogic\User $user */

ZF2 - BjyAuthorize - How to Get Rules and Guards from a Database

I'm using BjyAuthorize with Zend Framework2 to implement authorization and was able to successfully integrate roles from database. Now I want to get my Rules and Guards also from data base tables. How can I do this?
The easiest method and "the trick" here is really to:
Get your rules and guards into the same array format as it is shown in example configuration. So after reading records from the database, in whatever format your raw database data is, process it to match the same guard format as in the configuration. (My answer goes into detail on how to do that with Doctrine ORM, but also should give you an idea with other DB engines. Just substitute "DB read" operation with your fave database engine)
Inject the rules that are already in the proper format BjyAuthorize expects (because you made them so), into BjyAuthorize\Guard\Controller, from within YOUR_MODULE_NAME\Factory\DoctrineControllerGuardAdapterFactory, which you will write. Bjy's Controller will treat the rules as if those rules came from configuration*, and not suspect any difference.
Step back and enjoy!
This is the construct that you need to write in your own module:
namespace YOUR_MODULE_NAME\Factory;
/**
* See "How and where exactly to register the factory" in ZF2 config
* below in my answer.
*/
class [Custom]ControllerGuardAdapterFactory
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
/**
* Retrieve your rules from favorive DB engine (or anything)
*
* (You may use $serviceLocator to get config for you DB engine)
* (You may use $serviceLocator to get your DB engine)
* (You may use $serviceLocator to get ORM Entity for your DB engine)
* (Or you may hack your DB connection and retrieval in some other way)
*
* Hell, you may read them from a text file using PHP's file() method
* and not use $serviceLocator at all
*
* You may hardcode the rules yourself right here for all that matters
*/
$rules = ... //array(...);
/**
* Inject them into Bjy's Controller
*
* Rules must be in the same format as in Bjy config, or it will puke.
* See how ['guards'][\BjyAuthorize\Guard\Controller::class] is constructed
* in Bjy configuration for an example
*/
return new \BjyAuthorize\Guard\Controller($rules, $serviceLocator);
}
}
Now watch and observe how mind-numbingly complicated this can be made! (modeled after Bjy's own mechanisms)
This is mostly just ZF2, OO & Bjy "Configuration Hell", folks, nothing special otherwise. Welcome to ZF2 and Bjy and ORM Configuration Hell. You are welcome.
Detailed Answer - How to Implement?
Write an adapter factory, which reads rules from database, and then injects them into BjyAuthorize's Controller Guard. The effect will be the same as if the rules were being read from ['guards'][\BjyAuthorize\Guard\Controller::class]
What?
The way BjyAuthorize's Controller Guard works is it takes rules in a certain format (format specified for ['guards']['BjyAuthorize\Guard\Controller']), and then it uses the rules to populate the ACL. It also computes Resources from rules for you and loads those into ACL as well. If it didn't, you would have to write your own Resource Provider to do so.
So the task becomes:
Load rules from database and Transform the rules to format BjyAuthorize expects. This can be done in your own Rule Provider, much like this one.
You can use a factory to load your particular DB and storage class configuration arrays from module.config.php file. I put mine under ['guards']['YOUR_MODULE_NAME_controller_guard_adapter'].
'guards' => array(
'YOUR_MODULE_NAME_controller_guard_adapter' => array(
'object_manager' => 'doctrine.entity_manager.orm_default',
'rule_entity_class' => 'YOUR_MODULE_NAME\Entity\ObjectRepositoryProvider'
)
)
(cont) I put it under guards as opposed to rule_providers, because what we are dealing with here is not a pure rule provider. It is a guard provider, or "an adapter that gets rules out of your ObjectRepositoryProvider, and injects them into controller guard". This factory should look similar to this, except that you will be loading rules, not roles. You will then be injecting the rules into Controller, as in the next step.
Inject the rules into Controller, very much like it is done here
Example Implementation Details (from Q/A in comments)
More on the last point of "Injecting rules into Controller". Basically two steps: 1) make sure you already have (or will) generate your rules somehow (that's the hard step ). 2) inject those rules into controller (that's the easier step). The actual injection is done like this
$rules = __MAGIC__; //get rules out of somewhere, somehow.
return new Controller($rules, $serviceLocator); //$rules injection point
See code block below for my own implementation, where the last line in the block is the line I gave just above here.
namespace YOUR_MODULE_NAME\Factory;
use BjyAuthorize\Exception\InvalidArgumentException;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use YOUR_MODULE_NAME\Provider\Rule\DoctrineRuleProvider; //this one's your own
use BjyAuthorize\Guard\Controller;
class DoctrineControllerGuardAdapterFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
//just setting up our config, move along move along...
$config = $serviceLocator->get('Config');
$config = $config['bjyauthorize'];
//making sure we have proper entries in our config...
//move along "nothing to see" here....
if (! isset($config['guards']['YOUR_MODULE_NAME_controller_guard_adapter'])) {
throw new InvalidArgumentException(
'Config for "YOUR_MODULE_NAME_controller_guard_adapter" not set'
);
}
//yep all is well we load our own module config here
$providerConfig = $config['guards']['YOUR_MODULE_NAME_controller_guard_adapter'];
//more specific checks on config
if (! isset($providerConfig['rule_entity_class'])) {
throw new InvalidArgumentException('rule_entity_class not set in the YOUR_MODULE_NAME guards config.');
}
if (! isset($providerConfig['object_manager'])) {
throw new InvalidArgumentException('object_manager not set in the YOUR_MODULE_NAME guards config.');
}
/* #var $objectManager \Doctrine\Common\Persistence\ObjectManager */
$objectManager = $serviceLocator->get($providerConfig['object_manager']);
//orp -- object repository provider
//here we get our class that preps the object repository for us
$orp=new DoctrineRuleProvider($objectManager->getRepository($providerConfig['rule_entity_class']));
//here we pull the rules out of that object we've created above
//rules are in the same format BjyAuthorize expects
$rules=$orp->getRules();
//here pass our rules to BjyAuthorize's own Guard Controller.
//It will not know the difference if we got the rules from Config or from Doctrine or elsewhere,
//as long as $rules are in the form it expects.
return new Controller($rules, $serviceLocator);
}
}
DoctrineRuleProvider
namespace YOUR_MODULE_NAME\Provider\Rule;
use Doctrine\Common\Persistence\ObjectRepository;
use BjyAuthorize\Provider\Rule\ProviderInterface;
/**
* Guard provider based on a {#see \Doctrine\Common\Persistence\ObjectRepository}
*/
class DoctrineRuleProvider implements ProviderInterface
{
/**
* #var \Doctrine\Common\Persistence\ObjectRepository
*/
protected $objectRepository;
/**
* #param \Doctrine\Common\Persistence\ObjectRepository $objectRepository
*/
public function __construct(ObjectRepository $objectRepository)
{
$this->objectRepository = $objectRepository;
}
/**
* Here we read rules from DB and put them into an a form that BjyAuthorize's Controller.php understands
*/
public function getRules()
{
//read from object store a set of (role, controller, action)
$result = $this->objectRepository->findAll();
//transform to object BjyAuthorize will understand
$rules = array();
foreach ($result as $key => $rule)
{
$role=$rule->getRole();
$controller=$rule->getController();
$action=$rule->getAction();
if ($action==='all') //action is ommitted
{
$rules[$controller]['roles'][] = $role;
$rules[$controller]['controller'] = array($controller);
}
else
{
$rules[$controller.':'.$action]['roles'][]=$role;
$rules[$controller.':'.$action]['controller']=array($controller);
$rules[$controller.':'.$action]['action']=array($action);
}
}
return array_values($rules);
}
}
Q: How and where exactly to register the factory DoctrineControllerGuardAdapterFactory
A: Try this path: module\YOUR_MODULE_NAME\config\module.config.php and have
'service_manager' => array(
'factories' => array(
'YOUR_MODULE_NAME_controller_guard_adapter' => \YOUR_MODULE_NAME\Factory\DoctrineControllerGuardAdapterFactory::class
)
)
Note: YOUR_MODULE_NAME. The thing on the left of => sign is "the key", and can be anything you want it to be. Convention in Bjy is that it is similar to the actual class names and paths. And the thing on the right of the => is the actual fully qualified namespace to the class that you want to call with with this key.
Basically you have to write your own Provider.
Check out the different RoleProvider. Every RoleProvider implements the Provider\Role\ProviderInterface. The same thing has to be done when you want to implement Guards and Rules. You go into the specific directories Provider\Rule and Provider\Resource and check for the specific ProviderInterface.
That way you can write your own class implementing the Interface and then via configuration you tell BjyAuthorize to use your provider-classes.
As far as Guards are concerned, i do believe it is not yet possible to create those from Database. You would have to modify / PR the Module itself to make that happen.

How do I include or autoload external libraries in a TYPO3 Extbase Extension? + Dependecy Injection?

I'm developing a TYPO3 4.6 Extension with Extbase 1.4 and im trying to include an external library. The library, in my case the facebook PHP SDK, is under $_EXTKEY/Resources/PHP/facebook-php-sdk/facebook.php. I would like the library to autoload and automatically inject (Dependecy Injection) where I need it.
Some comments I found online suggest that one should include libraries with require_once():
http://forge.typo3.org/issues/33142
if it's just a tiny helper library, it's intended to be stored in {PackageRoot}/Resources/PHP/{libraryName} and just included via require. is this suspected by the problem however?
if the FLOW3 package mainly represents the foreing library at all, like it's the case in Imagine or Swift package, the library code is put below {PackageRoot}/Classes directly."
http://lists.typo3.org/pipermail/typo3-project-typo3v4mvc/2011-July/009946.html
"I would include the class (using require_once) from within a specific action to handle this. That way you have access over those functions and the class becomes your library."
I tried this and it works like this:
<?php
require_once( t3lib_extMgm::extPath('extkey') . 'Resources/PHP/facebook-php-sdk/facebook.php');
class Tx_WsLogin_Domain_Repository_FacebookUserRepository extends Tx_WsLogin_Domain_Repository_UserRepository {
protected $facebook;
public function __construct() {
$this->setFacebook(new Facebook(array(
'appId' =>'',
'secret' => '')
));
parent::__construct();
}
public function setFacebook(Facebook $facebook) {
$this->facebook = $facebook;
}
public function sampleFunction() {
$userId = $this->facebook->getUser();
}
}
?>
But how can I get it to autoload and automatically inject the library with the injectFacebook function?
edit:
Like #alex_schnitzler and #sorenmalling mentioned about autoloading:
#PeterTheOne Put all the files inside ext_autoload.php and then use DI or the object manager.
#PeterTheOne put the class definition into ext_autoload.php in your extension?
I tried it like this (file: ext_autoload.php):
<?php
$extPath = t3lib_extMgm::extPath('extKey');
return array(
'facebook' => $extPath . 'Resources/PHP/facebook-php-sdk/facebook.php',
);
?>
It seems to find and include the right file. But when I try to user Dependency Injection (like peter answered) I get an error:
not a correct info array of constructor dependencies was passed!
InvalidArgumentException thrown in file /var/syscp/webs/web1/dev/typo3_src-4.5.15/typo3/sysext/extbase/Classes/Object/Container/Container.php in line 247.
I think this is because the constructor of the Facebook class has a required $config argument.
edit2:
I did what peter said in his answer and with the help of #alex_schnitzler and #sorenmalling, who pointed me to the ObjectManager, my FacebookService looks like this now:
class Tx_Extkey_Service_FacebookService implements t3lib_Singleton {
/**
* #var Tx_Extbase_Object_ObjectManagerInterface
*/
protected $objectManager;
/**
* Facebook from #link https://github.com/facebook/facebook-php-sdk facebook-php-sdk
*
* #var Facebook
*/
protected $facebook;
/**
* #param Tx_Extbase_Object_ObjectManagerInterface $objectManager
*/
public function injectObjectManager(Tx_Extbase_Object_ObjectManagerInterface $objectManager) {
$this->objectManager = $objectManager;
}
/**
*
*/
public function initializeObject() {
$this->facebook = $this->objectManager->create(
'Facebook',
array(
'appId' =>'input appId here',
'secret' => 'input app secret here'
)
);
}
/**
* #return Facebook
*/
public function getFacebook() {
return $this->facebook;
}
}
For more help read: http://forge.typo3.org/projects/typo3v4-mvc/wiki/Dependency_Injection_(DI) the parts about initializeObject() and Creating Prototype Objects through the Object Manager
First create ext_autoload.php in extension root folder
and add your code,it contain single dimension array with key as class name(class name must be prefix with extension key) and value as path to file.
make sure clear your site
<?php
$extensionPath = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('rent_system');
return array(
'rent_system_TCPDF' => $extensionPath.'Resources/Private/PHP/tcpdf/tcpdf.php',
);
?>
In controller file
$pdf = $this->objectManager->create('rent_system_TCPDF');
Extbase injection is pretty simple. Here's the actual implementation. Using external libraries, however, is not.
Once you figure out how to load the library, have you tried just injecting it? Like so:
/**
* #var Facebook
*/
protected $facebook;
/**
* inject the facebook
*
* #param Facebook facebook
* #return void
*/
public function injectFacebook(Facebook $facebook) {
$this->facebook = $facebook;
}
NOTE: You need the #param in the comment and you also need to clear your configuration cache after adding this code.
I don't know about the Facebook SDK API, but hopefully you can instantiate the Facebook object with the default constructor and then add the arguments later with setter methods. You might want to create a FacebookService class (singleton) that loads the Facebook PHP and sets the essential arguments. Then you can inject a FacebookService to get the actual Facebook object whenever you need it.

can't turn of strict checking on include of duplicate constructor class - php 5.3

I have a system where Joomla and Symfony Frameworks work together. In a specific situation I need to include a range of files in Joomla from within Symfony. The problematic Joomla file has a "duplicate constructor" for PHP4 compatibility purposes, like so:
class JObject{
/**
* An array of errors
*
* #var array of error messages or JExceptions objects
* #access protected
* #since 1.0
*/
var $_errors = array();
/**
* A hack to support __construct() on PHP 4
*
* Hint: descendant classes have no PHP4 class_name() constructors,
* so this constructor gets called first and calls the top-layer __construct()
* which (if present) should call parent::__construct()
*
* #access public
* #return Object
* #since 1.5
*/
function JObject()
{
$args = func_get_args();
call_user_func_array(array(&$this, '__construct'), $args);
}
/**
* Class constructor, overridden in descendant classes.
*
* #access protected
* #since 1.5
*/
function __construct() {}
When I include this, I get an error
Strict Standards: Redefining already defined constructor
From what I can find on php.net I should be able to turn Strict standards off like this, but it doesn't work:
error_reporting(error_reporting() & (E_ALL ^ E_STRICT));
In your apps/<app name>/config/settings.yml you should be able to set the error_reporting level. Something like:
all:
.settings:
error_reporting: <?php echo (E_ALL | E_STRICT)."\n" ?>

Resources