Typo3: Controller action with hidden record as its parameter - typo3

I am currently trying to get a extension working in Typo3 v10, which enables the user to show, edit, update, disable and enable other user accounts.
Unfortunately, I am running into the issue that I cannot use disabled users as arguments for the actions:
/**
* Save user changes
*
* #param \Company\MyExtension\Domain\FeUser $feuser
* #return void
*/
public function updateAction(\Company\MyExtension\Domain\FeUser $feuser): void {}
It would resulting in the error following error:
Object of type \Company\MyExtension\Domain\FeUser with identity "3" not found.
From what I have gathered, extbase does not rely on the repository's default query settings to create the objects but instead uses PersistenceManager::createQueryForType to get the query settings via QueryFactory::create. This is an issue also listed in a few bug reports.
There's a suggestions as to how to use a custom QueryFactory to set different default QuerySettings for my extension, however this does not seem to work in Typo3 v10 anymore, at least my custom QueryFactory isn't being used after registering it... Also, if this were to work, wouldn't it use the new QueryFactory for all objects instantiated through a controller action and not just this extension?
How can I properly handle hidden users my extension with Typo3 v10.4?
P.S. Another suggestion was to fetch object early through initializeAction, but this only works well if it is about the unmodified model and not when setting new values for the object, as it would just load the database values in the end...

Checkout the extension "news" how it is handled here:
if ($news === null || $this->settings['isShortcut']) {
$previewNewsId = ((int)$this->settings['singleNews'] > 0) ? $this->settings['singleNews'] : 0;
if ($this->request->hasArgument('news_preview')) {
$previewNewsId = (int)$this->request->getArgument('news_preview');
}
if ($previewNewsId > 0) {
if ($this->isPreviewOfHiddenRecordsEnabled()) {
$news = $this->newsRepository->findByUid($previewNewsId, false);
} else {
$news = $this->newsRepository->findByUid($previewNewsId);
}
}
}
https://github.com/georgringer/news/blob/master/Classes/Controller/NewsController.php#L338

I am assuming you need the URL as well. I gave an answer here if you wanna take a look.
Show, edit and update hidden records in FrontEnd (TYPO3)
You can adjust it to your own needs as well.
Best regards

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.

How can I use Spatie Media Library with Backpack for Laravel upload field?

I want to upload a file in Backpack for Laravel and immediately attach the file to the model using Spatie Media Library.
What is my best option to do this?
I tried doing this using the SetImageAttribute mutator as recommended in the docs: https://backpackforlaravel.com/docs/4.1/crud-fields#upload-1
public function setImageAttribute($value) {
$this->addMedia('image');
}
However this method requires me to have an unused "image" field on the DB (because Spatie Media Library uses its own table).
Is there another way to do this without using a mutator/creating extra DB fields?
You can avoid having to create an unused image field on your DB by overwriting both the set and get mutators/accessor on your model.
Laravel documentation defining accessor
Laravel documentation defining mutator
Create the following 2 methods on your model:
public function getImageAttribute()
{
return $this->getFirstMediaUrl('image') ?? '';
}
public function setImageAttribute($file)
{
$this->clearMediaCollection('image'); // to remove any previous files
if ($file) {
$this->addMedia($file)->usingFileName($file->hashName())->toMediaCollection('image');
}
}
If you have a fillable set on your model, also add image to it.
protected $fillable = ['image']
Now you should be able to create and update the files using Backpack.
You may change the implementation based on your needs and requirements.

showAction : finding fe_user entry by username instead of uid

I created my own (Typo3 6.2) extension with the Extension Builder to extend fe_users and all is working fine but I would like to be able to use the showAction based on the username instead of the uid of the user.
The goal is simply to be able to call the controller on the frontend like this:
my.domain.com/users?tx_crilcq_crilcqmember[username]=johndoe&tx_crilcq_crilcqmember[action]=show
instead of this:
my.domain.com/users?tx_crilcq_crilcqmember[member]=2&tx_crilcq_crilcqmember[action]=show
At first I thought that I had to modify the extended repository (class MemberRepository extends \TYPO3\CMS\Extbase\Persistence\Repository) but it seems that it is used for requests returning an array of entries and not to reference a single instance. Don't know if I'm right about this but my feeling is that the thing I need to modify is somewhere else.
Any clue someone?
An Extbase repository has magic methods findBy[property], findOneBy[property] and countBy[property], that returns/counts objects by the given property.
You can change your show action as shown below:
/**
* #param string $username
* #return void
*/
public function showAction($username) {
$user = $this->memberClassRepository->findOneByUsername($username);
$this->view->assign('user', $user);
}

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

TYPO3 extbase - get "uid" of non persisted object

Just started out with the frst extbase extension. In localconf I added these actions: 'list, new, show, create, edit'.
Default was that "create" redirected to "list" with no arguments, and that worked fine right after creation of the extension.
$this->redirect('list'); // <- this works if used
But instead of redirecting to "list" I would like to redirect to "show" to display the newly added priceFormCalc. A collegue helped me to acomplish this using persist.
Below is the code and it works. But reading on the net it seem that it should not be best practice to run persist. It should be doable to call the action show without manually persisting first.
So question is: Is this the correct way to do it, or is there a more "ext base" way show a newly created record?
public function createAction(\TYPO3\OgNovomatrixpricecalc\Domain\Model\PriceCalcForm $newPriceCalcForm) {
$this->priceCalcFormRepository->add($newPriceCalcForm);
$this->flashMessageContainer->add('Your new PriceCalcForm was created.');
// workaround - or the right way?
$persistenceManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('Tx_Extbase_Persistence_Manager');
$persistenceManager->persistAll(); // <- save i database
$uid = $newPriceCalcForm->getUid(); // <- get UID of newly saved record
// do redirect using uid of newly created priceCalcForm
$this->redirect('show',Null,Null, array('priceCalcForm' => $uid));
}
To
You can persist the current state and then get the uid. Inject the configuration manager (TYPO3 6.x way):
/**
* persistence manager
*
* #var \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface
* #inject
*/
protected $persistenceManager;
Then use
$this->persistenceManager->persistAll();
to apply all changes. Then you can pass your object (or only the uid) to the action.