php Transpose an array

oder anders ausgedrückt, „rotate a multidimensional array“

Das hier fällt in die Kategorie TIL.

In einem großartigen Beitrag von Adam Wathan habe ich gelernt das man PHP Arrays relativ einfach rotieren kann. Darüber bin ich dann über einen StackOverflow Beitrag gestolpert, der es nochmals sehr vereinfacht hat.

Dazu ein sehr einfaches Beispiel, etwas nützlicheres bei Adam.


$before = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
];

function transpose($array) {
    return array_map(null, ...$array);
}

$after = transpose($before);

//  $after = [
//      [1, 4, 7],
//      [2, 5, 8],
//      [3, 6, 9],
//  ];

print_r($before);
print_r($after);

TYPO3 11.5 Update Backend nicht erreichbar -> notwendiges htaccess Update

Für die Integration der #93048 – Backend URL rewrites muss die htaccess angepasst werden. Eigentlich sollte es automatisch passieren, doch wenn die .htaccess zu sehr angepasst wurde funktioniert der Automatismus nicht mehr.

Apache config before:

RewriteRule ^(?:typo3/|fileadmin/|typo3conf/|typo3temp/|uploads/) - [L]

Apache config after:

RewriteRule ^(?:fileadmin/|typo3conf/|typo3temp/|uploads/) - [L]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^typo3/(.*)$ %{ENV:CWD}typo3/index.php [QSA,L]



Quelle:
https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.0/Breaking-93048-BackendURLRewrites.html

TYPO3 Urls erstellen, in FE, BE oder CLI

Als Entwickler hat man immer wieder mal die Aufgabe Links zu generieren, wenn man im Backend oder mit der CLI/Scheduler unterwegs ist um. Wenn die Links ins Frontend führen sollten, musste man immer erst das TSFE erzeugen um dann über typolink alles zu erzeugen.

Früher…., *ich werf mal einen Stein*…. (ja soviel früher), konnte man dafür die TYPO3 Erweiterung PagePath nutzen, ursprünglich von Dmitry Dulepov geschrieben und dann von Sebastian Michaelsen übernommen und für eine weitere TYPO3 Version geupdatet. …. Wenn man die Erweiterung selbst geupdatet hat, dann kann man sie auch noch in TYPO3 9 nutzen.

Doch man will ja nicht ständig patchen sondern irgendwann einfach mit dem Core Links erzeugen, von daher GoodBye PagePath and Hello PageRouter

Nach einer kurzen Suche zu dem Thema landet man natürlich auf …. genau Stackoverflow (wahrscheinlich die zweit häufigste Seite, nach Google, eines jeden Developers) und hier dann genau auf bei einer Erklärung von Mathias Brodala

Ich denke mal jeder der im TYPO3 im Slack Channel oder auf Stackoverflow was TYPO3 spezifisches gefragt hat, bekam von Ihm schon mal eine schnelle, freundliche fundierte Antwort 🙂 Man kann daher davon ausgehen, wenn er das empfiehlt dann passt das.

Hier noch mal kurz in Code :

Für die Linkerzeugung, tauschen wir hier nun den alten PagePath Code aus:

$arguments = '&tx_nimeinenAltePiBaseExtension_pi1[showUid]=' . (int)$record['uid'];
$singleViewPageUid = '100';
$singleViewLink =  \tx_pagepath_api::getPagePath($singleViewPageUid, $arguments);
 

und nutzen dann wie auf dem StackOverflow Beitrag den Code von Mathias.

use TYPO3\CMS\Core\Site\SiteFinder;
use TYPO3\CMS\Core\Utility\GeneralUtility;

....


$arguments = [
 'tx_nimeinenAltePiBaseExtension_pi1' => ['showUid' => (int)$record['uid']],
];
$singleViewPageUid = '100';
$site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId($singleViewPageUid);

$singleViewLink = (string)$site->getRouter()->generateUri((string)$singleViewPageUid, $arguments);

Kleine Anpassung noch von mir, sofern es keine Multidomain TYPO3 Installation ist kann man das „suchen“ nach der richtigen $site etwas vereinfachen wenn man statt wie oben die SiteConfiguration per $singleViewPageUid sucht, direkt den Namen der Configuration nutzt, also

$site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByIdentifier('test');

Den Namen kann man im Modul Sites auslesen, das ist dann auf der Übersichtsseite der „Configuration Folder“

Für alle die Wissen wollen warum es einfacher wäre, ein Blick in den Code verrät warum es.
->getSiteByPageId vs ->getSiteByIdentifier

getSiteByPageId() ist aber einfacher (und damit auch sicherer) in der Handhabung, je nach Umgebung, Konfigurationen der Systeme und im Einsatz in verschiedenen Erweiterungen.

Benutzung von .env in TYPO3

Das ist eine 1 zu 1 Kopie, von diesem Snippet https://gitlab.apfelkiste.eu/snippets/1
da ich mir den Link nicht merken kann, das ganze hier noch mal gespeichert 🙂

Mit Hilfe einer .env-File können empfindliche Einstellungen wie Passwörter bzw. Einstellungen, die sich je System ändern zentral und außerhalb des Repositories abgelegt werden.

Composer-Install

composer req helhum/dotenv-connector 

.env-File

TYPO3_CONTEXT="Development"
TYPO3__DB__database="base9"
TYPO3__DB__host="127.0.0.1"
TYPO3__DB__password="geheim"
TYPO3__DB__port="3306" 
TYPO3__DB__username="root" 
SOLR_PASSWORD="psst!geheim" 

public/typo3conf/LocalConfiguration.php

...
'DB' => [
    'Connections' => [
        'Default' => [
            'charset' => 'utf8mb4',
            'dbname' => '<set by dotenv>',
            'driver' => 'mysqli',
            'host' => '<set by dotenv>',
            'password' => '<set by dotenv>',
            'port' => '<set by dotenv>',
            'tableoptions' => [
                'charset' => 'utf8mb4',
                'collate' => 'utf8mb4_unicode_ci',
            ],
            'user' => '<set by dotenv>',
        ],
    ],
],

public/typo3conf/AdditionalConfiguration.php

<?php
$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['dbname'] = getenv('TYPO3_DB_NAME');
$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['host'] = getenv('TYPO3_DB_HOST');
$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['password'] = getenv('TYPO3_DB_PASSWORD');
$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['user'] = getenv('TYPO3_DB_USER');

Benutzung in Site-Config

rootPageId: 1
base: 'https://%env(SAAROBERMOSEL_DOMAIN)%/'

Benutzung von TYPO3_CONTEXT in typoscript

# Einstellungen für Entwicklunssysteme
[applicationContext = Development]
  config.absRefPrefix = http://projekt.dev/
  config.admPanel = 1
  [...]
[end]

Benutzung im Typoscript (Werte)

plugin.tx_solr.solr {
	read {
		password = TEXT
		password {
			data = getenv:SOLR_PASSWORD
		}
	}
}

composer – good to know

Bei mir entstehen immer wieder Fragen rund um TYPO3, einige Fragen und die Antworten dazu:

Diese Fragen kommen aus dem Slack Channel #typo3-cms-composer
die Fragen habe nicht ich geschrieben, jedoch mir selbst gestellt 🙂

how do i see which dependencies are currently active on my packages in composer?
composer depends <package-name>

he problem is that i get TYPO3 10.4.9 instead of the latest. 
composer why-not typo3/cms-core 10.4.11

Update den TYPO3 Core mit allen Abhängigkeiten:
composer update typo3/* --with-all-dependencies

Zeige installierte Versionen:
composer show

Replacing master with main in Github

Folgendes habe ich auf dev.to gefunden von Alexis Moody

Step 1 – Update Local

$ git branch -m master main
$ git push -u origin main

So what are we doing here? First with the -m command we are moving the git history from master to a new branch called main. Next we’re pushing the main branch up to the origin remote, and establishing an upstream connection with the -u command.

Step 2 – Update Repo

  • Navigate to your repository > Settings > Branches
  • Select main as your default branch
  • Update your branch protection rules
  • Navigate to Code > Branches and delete master

TYPO3 9 Conditions

Durch die Symfony Expression Language hat sich ja einiges geändert, daher hier eine lose Sammlung wie die alten TypoScript Bedinungen ersetzt werden können

Zuerst hier schon mal 2 Blogs die das schon großartig umschreiben

und schon mal eine ganz wichtige aus GP: und einem nested Array wird folgendes

[globalVar = GP:tx_ttnews|tt_news > 0]
//POST && GET
[request.getParsedBody()['tx_news_pi1']['news'] > 0 || request.getQueryParams()['tx_news_pi1']['news'] > 0]

//only GET
[request.getQueryParams()['tx_news_pi1']['news'] > 0]
//mittlerweile ist diese Variante zu bevorzugen, da so keine exceptions in //der Log File geworfen werden wenn kein 'tx_news_pi1' in der aktuellen Anfrage vorhanden ist

[traverse(request.getQueryParams(), 'tx_news_pi1/news') > 0] # This condition matches if current query parameters have tx_news_pi[news] set to a value greater than zero [END]
[globalVar = TSFE:id=1]

[page["uid"] == 1]

bzw.

[getTSFE().id == 1]
[globalVar = TSFE:beUserLogin > 0]

[getTSFE() && getTSFE().isBackendUserLoggedIn()]
[PIDupinRootline = 30]

//TYPO3 9.5.x
[30 in tree.rootLineIds && page["uid"] != 30]

// TYPO3 10
[30 in tree.rootLineParentIds]

//source https://forge.typo3.org/issues/88962

TYPO3 9 404 aus Extensions heraus

in früheren Version von TYPO3 konnte man einfach folgenden Code nutzen damit ein entsprechender 404 geworfen wird.

$GLOBALS['TSFE']->pageNotFoundAndExit('text');

Mit TYP3 9 wird folgendes empfohlen:

            return GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
                $this->request,
                'The requested page does not exist',
                ['code' => PageAccessFailureReasons::PAGE_NOT_FOUND]
            );

Ich nutze es nun immer in Zusammenhang mit einer DetailAnsicht welche meist auf einer eigenen Unterseite der Listen Ansicht, in folgender Art und Weise:

  public function detailAction(MyObject $event = null)
  {
      if ($event === null) {
            $this->handleContentNotFound();
        }
   ...
 }



/** 
     * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
     * @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException
     */
    public function handleContentNotFound()
    {
        $isThereAnEventId = $this->request->hasArgument('event');

        if ($isThereAnEventId === true) {
            return GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
                $this->request,
                'The requested page does not exist',
                ['code' => PageAccessFailureReasons::PAGE_NOT_FOUND]
            );
        }
        $parentPageUrl = $this->uriBuilder
            ->setTargetPageUid($this->getTypoScriptFrontendController()->page['pid'])
            ->setCreateAbsoluteUri(true)
            ->build();
        $this->redirectToUri($parentPageUrl, 0, 301);
    }

In der detailAction wird zuerst geprüft ob das injecten des Objectes NICHT erfolgreich war, also weiterhin null ist.
Wenn dem der Fall ist geht es weiter zu „handleContentNotFound()“.
Dort wird geschaut, ob mit dem request überhaupt eine Anfrage nach einer SingleView gestellt wurde, also ob eine, in meinem Fall „event“ Variable mit übergeben wurde. Wenn dem so ist, existiert das Event nicht oder ist bereits abgelaufen. Ansonsten gehen wir davon aus, das die nur die DetailSeite aufgerufen wurde und diesen Aufruf leiten wir mit einem 301 auf die übergeordnete Seite, also die Listenansicht weiter.

TYPO3 9 class für UL im ckeditor

in TYPO3 8 funktioniert bisher folgende Erweiterung der lib.parsefunc_RTE um dem ListenElement eine CSS Class mit geben zu können.

lib.parseFunc_RTE {
externalBlocks {
ul.stripNL = 1
ul.callRecursive = 1
ul.callRecursive.tagStdWrap.HTMLparser = 1
ul.callRecursive.tagStdWrap.HTMLparser.tags.ul {
fixAttrib.class.default = rteContentList
}
}
}

Allerdings gibt es jetzt in TYPO3 9 Probleme damit, es sieht ungefähr so aus:

<ul>
	<p></p>
	<li>...</li>
	<li>...</li>
	<li>...</li>
	<p><p>
</ul>	

das scheint im Moment zu funktionieren:

lib.parseFunc_RTE.externalBlocks.ul.stdWrap{
  HTMLparser = 1
  HTMLparser {
    tags.ul.fixAttrib.class {
      default = rteContentList
      always = 1
      list = rteContentList
    }
    keepNonMatchedTags = 1
  }
}

es ist in etwa gleich.