TYPO3 queryBuilder, how to let PhpStorm recognise methods? - typo3

For example
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
$queryBuilder = $connectionPool->getQueryBuilderForTable($table);
$statement = $queryBuilder
->select('uid')
->from($table)
->orderBy('start_date', 'DESC')
->where(
$queryBuilder->expr()->lte('start_date', $queryBuilder->createNamedParameter($startDate, \PDO::PARAM_INT)),
$queryBuilder->expr()->neq('uid', $queryBuilder->createNamedParameter($currentUid, \PDO::PARAM_INT))
)
->setMaxResults(1)
->execute();
while ($row = $statement->fetch()) {
$prevs[] = $row;
}
How can one let PhpStorm recognise the methods select, expr, createNamedParameter, fetch etc.

PhpStorm doesn't automatically know what class $connectionPool is and so can't know what class everything derived from that is. You can tell PhpStorm what class $connectionPool is by adding an annotation:
/** #var ConnectionPool $connectionPool */
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);

If you use the TYPO3 Plugin for PhpStorm, it's able to get the correct information from makeInstance() (and many more features).
https://plugins.jetbrains.com/plugin/9496-typo3-cms-plugin

You can do 2 steps to help PhpStorm recognise functions outside of your project:
Annotate the variables with their respective namespace:
/** #var $queryBuilder \TYPO3\CMS\Core\Database\Query\QueryBuilder **/
$queryBuilder = $connectionPool->getQueryBuilderForTable($table);
You can shorten the above when used multiple time with declaring:
use \TYPO3\CMS\Core\Database\Query\QueryBuilder;
at the top of your file, the you would just need
/** #var $queryBuilder QueryBuilder **/
As a second step include the Typo3 sources to your project (if they are not inclued in your project files already) so PhpStorm will be able to index them

I find that adding annotations for the variable type is unnecessary and just clutters up the source code.
In general, for PhpStorm to find the classes, methods etc. you have to include the TYPO3 source in your project.
You can create a PHP project which includes your entire web root (typically "public" folder), but excludes directories like filadmin, uploads, typo3temp etc. It is important to exclude these directories so PhpStorm does not unnecessarily scan them. (In particular if you run the core functional tests this is very much recommended.) I find this is the most practical approach and also useful for debugging and core development
You can create a PhpStorm project for your extension and include your TYPO3 source as well. Don't know about this off the top of my head, but should be possible.
Once you do this and PhpStorm scanned your files, it should be possible to do any of the following:
Auto-expand class and function names
Add use statements automatically (see PhpStorm help for more)
etc.
See also DynamicReturnType plugin and other plugins recommended for TYPO3 core development. The DynamicReturnType plugin along with the file dynamicReturnTypeMeta.json shipped with the core should give PhpStorm enough information for return type of classes instantiated with makeInstance for example.

Related

How to change the position of a column in the TYPO3 TCA?

I want to move a column from one tab to another. Is there a better way than to rewrite the types definition manually which is not very reliant in regard to extension updates changing the TCA?
It's not 100% fail safe, since there still might be extensions messing things up, but the closest you can get will be to make use of the ExtTablesInclusionPostProcessing event and put addToAllTCAtypes into the invoke method, since this will not just add new fields but even put existing fields to another tab:
<?php
declare(strict_types=1);
namespace Vendor\Extension\EventListener;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
/**
* Class ExtTablesInclusionPostProcessing
*/
class ExtTablesInclusionPostProcessing
{
/**
* Function which may process data created / registered by extTables
* scripts (f.e. modifying TCA data of all extensions)
*
* #param AfterTcaCompilationEvent $event
*/
public function __invoke(AfterTcaCompilationEvent $event): void
{
ExtensionManagementUtility::addToAllTCAtypes('tt_content', 'fieldname', '', 'after:fieldname_in_the_other_tab');
}
}
To remove the existing field before, you could make use of this trick:
https://stackoverflow.com/a/59179916/2333754
And finally to be on the safer side you could make sure that other extensions are loaded before your own extension by putting them into the suggest section of your composer.json.
With installation of extension tcabuilder it is possible to do simple changes in the TCA.

TYPO3 - includelibs security

I need to include a PHP script in my TS template :
page {
10 = USER_INT
10.includeLibs = lib_confidential.php
10.userFunc = MyClass->ConfidentialRequest
}
It works perfectly but I would like to locate the lib_confidential.php outside of my website root directory (and make something like 10.includeLibs = ../lib_confidential.php). Is it possible to secure my PHP script and how to ? I thought about creating a symlink but that doesn't give any solution.
As your installation needs an update you will have to change the mechanism for including php-functions for the future.
since TYPO3 8 you need to have a class for your php functions. So the autoloader can identify the class and execute the function you need to place the class inside of an extension or declare the class to the autoloader.
Best practice would be site extension where you configure your installation, there you can havea class with all the functions you need.
examples can be found in the manual.

Retrieving the BackendUser from BackendUserAuthentication

I'm trying to develop an extension that adds a Button to the ClearCache menu in the TYPO3 Backend. In a large installation with multiple domains, non-admin users need a button to clear the page cache for their domain, but only of those pages that they have access to. The default options.clearCache.pages = 1 instead flushes the whole Frontend Cache of the installation.
I've gotten so far as to calling a method in a custom class ClearCacheHook, that implements \TYPO3\CMS\Backend\Toolbar\ClearCacheActionsHookInterface.
I next need to get a list of all page uids the BackendUser has access to, which is done with $backendUser->getDbMountPoints(). All the docs speak of a global variable $BE_USER, but this isn't set for me. I have a $GLOBALS['BE_USER'], but that is of the class BackendUserAuthentication.
I can't figure out how to resolve the BackendUser from the BackendUserAuthentication. Theres the BackendUser uid in the object so I tried initializing a TYPO3\\CMS\\Beuser\\Domain\\Repository\\BackendUserRepository via the ObjectManager, but that fails.
I'll focus on the more specific tasks: Create an instance of BackendUserRepository and create an instance of BackendUser from uid.
You might have a look at the UsernameViewHelper.php class of be_log in the TYPO3 core.
specifically this:
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Domain\Repository\BackendUserRepository;
use TYPO3\CMS\Extbase\Object\ObjectManager;
...
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
$backendUserRepository = $objectManager->get(BackendUserRepository::class);
/** #var \TYPO3\CMS\Extbase\Domain\Model\BackendUser $user */
$user = $backendUserRepository->findByUid($uid);
If there is something, I don't find in the documentation, I sometimes look at existing extensions. A good candidate is the news extensions. Other good candidates are, of course, the TYPO3 source code.

Fluid standalone view in BE context

Given you are in a BE or CLI context (e.g. sending emails via extbase command controller task), the following worked up to 7 LTS for rendering a fluid standalone view:
$view = $this->objectManager->get(StandaloneView::class);
$view->setTemplatePathAndFilename('/Absolute/Path/To/Template.html');
$view->setFormat('html');
$view->getRequest()->setControllerExtensionName('Myextensionname');
return trim($view->render());
However, in 8 LTS, this throws the following exception:
Tried resolving a template file for controller action "Standard->index" in format ".html", but none of the paths contained the expected template fileā€¦ No paths configured.
As suggested in the wiki page at https://wiki.typo3.org/How_to_use_the_Fluid_Standalone_view_to_render_template_based_emails#Usage_in_TYPO3_8.7, I tried setting layout and partial root paths for the view:
$view->setLayoutRootPaths(['EXT:Myextensionname/Resources/Private/Layouts/']);
$view->setPartialRootPaths(['EXT:Myextensionname/Resources/Private/Partials/']);
However, this won't do the trick.
Digging a bit deeper, I could imagine that one would have to set the controller and action name, e.g. by setting the controller context, but that does not seem to be a solid solution as multiple other class instances are tied to it.
What is the correct way to render fluid standalone views in 8 LTS?
Here an example from our current webproject where we want to show a simple note in backend context based on a FLUID HTML in TYPO3 8.7
protected function renderMarkup(): string
{
$standaloneView = GeneralUtility::makeInstance(StandaloneView::class);
$standaloneView->getRequest()->setControllerExtensionName('in2template');
$templatePathAndFile = 'EXT:in2template/Resources/Private/Templates/Tca/ToolbarNoteEmptyFields.html';
$standaloneView->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templatePathAndFile));
$standaloneView->assign('toolbar', 'toolbarstuff');
return $standaloneView->render();
}
StandaloneView likes to receive all template paths (partial, template and layout root paths) so if you don't already provide all of them, you should do so. The reason being that the naming "Standalone" refers to the view being tied neither to a specific MVC action nor a specific extension context.
That said, if you use 8.7.5 there's a chance you are hit by a regression that is going to be solved by https://review.typo3.org/#/c/53917/ so it might be worth checking that out before you do a major refactoring. Technically the StandaloneView can be operated like a TemplateView with extension context, it's just not officially supported behavior and TYPO3 may not consistently apply all of the context you expect.
In my 8.7 extension I use the following code to get the StandaloneView:
$extbaseFrameworkConfiguration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
/** #var StandaloneView $emailView */
$emailView = $this->objectManager->get(StandaloneView::class);
$emailView->getRequest()->setControllerExtensionName($controllerExtensionName);
$emailView->getRequest()->setPluginName($pluginName);
$emailView->getRequest()->setControllerName($controllerName);
$emailView->setTemplateRootPaths($extbaseFrameworkConfiguration['view']['templateRootPaths']);
$emailView->setLayoutRootPaths($extbaseFrameworkConfiguration['view']['layoutRootPaths']);
$emailView->setPartialRootPaths($extbaseFrameworkConfiguration['view']['partialRootPaths']);
$emailView->setTemplate('Email/' . ucfirst($templateName));
$emailView->assignMultiple($variables);
$emailBody = $emailView->render();
In my function I gave the $controllerExtensionName, $pluginName and $controllerName as parameter with default values, so that other controllers/plugins can use this function, too.

How to make extbase extension recognize storage page from plugin?

In an extbase extension built with extension builder on TYPO3 6.1.7, I haven't set any storagePid via Typoscript.
But I have set the "Record Storage Page" in the plugin:
I would expect that it would now only fetch records from this page.
But it doesn't, it just returns all items from that table.
How do I make the extension recognize the setting from the plugin?
Or (if it's supposed to do that out of the box) how do I find out why it doesn't?
I did a lot of research when my extension's frontend plugin (TYPO3 7.6.4) refused to use the 'pages' field of the plugin ("Record Storage Page"), so I would like to share my findings:
My extension's name is 'tx_dhsnews', my plugin's name is 'infobox'
setRespectStoragePage must be set to true (default): $query->setRespectStoragePage(TRUE)
In the typoscript-setup, the plugin-specific storagePid plugin.tx_dhsnews_infobox.persistence.storagePid MUST NOT be present at all! Not even with an empty value! Else the 'pages'-field will not be respected!
That's all. The Extensions Builder just created a typoscript-setup with the storagePid for the specific plugin 'infobox' set to nothing. That resulted in the plugin not respecting the 'pages' - field.
It's no problem to set the storagePid on extension-level (e.g. 'tx_dhsnews..persistence.storagePid'), the value will be merged with the value(s) given in 'pages' ("Record Storage Page"), but as soon the plugin-specific tx_[extension]_[plugin].persistence.storagePid exists in the typoscript, it will overrule everything else!
Hope this will help somebody to save some time + nerves
Add the following code to your repository
namespace <Vendor>\<Extkey>\Domain\Repository;
class ExampleRepository extends \TYPO3\CMS\Extbase\Persistence\Repository {
// Example for repository wide settings
public function initializeObject() {
/** #var $defaultQuerySettings \TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings */
$defaultQuerySettings = $this->objectManager->get('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Typo3QuerySettings');
// add the pid constraint
$defaultQuerySettings->setRespectStoragePage(TRUE);
}
// Example for a function setup changing query settings
public function findSomething() {
$query = $this->createQuery();
// add the pid constraint
$query->getQuerySettings()->setRespectStoragePage(TRUE);
// the same functions as shown in initializeObject can be applied.
return $query->execute();
}
}
You will find more informations at this page
http://forge.typo3.org/projects/typo3v4-mvc/wiki/Default_Orderings_and_Query_Settings_in_Repository
I actually modified my MVC controller to achieve a record filtering depending on the actual page(storagePid==page.id).. looked like this:
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class MyMVCController extends ActionController {
protected function initializeAction() {
parent::initializeAction();
//fallback to current pid if no storagePid is defined
$configuration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
if (empty($configuration['persistence']['storagePid'])) {
$currentPid['persistence']['storagePid'] = GeneralUtility::_GP('id');
$this->configurationManager->setConfiguration(array_merge($configuration, $currentPid));
}
[..]
I used the solution by http://wiki.t3easy.de/ and modified the value of the storagePid cause i developed a backend module.
His tscript example didnt worked for me..
Also an article by Thomas Deuling was interesting for the topic..
but i still dont get the whole connection into my mind.. wanna go back to symfony xD
edit: for the modification of the repo queries also this article looked interesting:
https://forge.typo3.org/projects/typo3v4-mvc/wiki/Default_Orderings_and_Query_Settings_in_Repository