Initial commit - Typo3 11.5.41

This commit is contained in:
Matteo Gallo
2026-07-03 17:53:31 +02:00
commit 5ca4743197
6811 changed files with 568848 additions and 0 deletions

View File

@@ -0,0 +1,128 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Service;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Information\Typo3Version;
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Service for category related stuff
*/
class CategoryService
{
/** @var TimeTracker */
protected $timeTracker;
/** @var FrontendInterface */
protected $cache;
public function __construct()
{
$this->timeTracker = GeneralUtility::makeInstance(TimeTracker::class);
$versionInformation = GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion();
$cacheIdentifier = $versionInformation >= 11 ? 'ttaddress_category' : 'cache_ttaddress_category';
$this->cache = GeneralUtility::makeInstance(CacheManager::class)->getCache($cacheIdentifier);
}
/**
* Get child categories by calling recursive function
* and using the caching framework to save some queries
*
* @param string $idList list of category ids to start
* @param int $counter
* @return string comma separated list of category ids
*/
public function getChildrenCategories(string $idList, int $counter = 0)
{
$cacheIdentifier = sha1('children' . $idList);
$entry = $this->cache->get($cacheIdentifier);
if (!$entry) {
$entry = $this->getChildrenCategoriesRecursive($idList, $counter);
$this->cache->set($cacheIdentifier, $entry);
}
return $entry;
}
/**
* Get child categories
*
* @param string $idList list of category ids to start
* @param int $counter
* @return string comma separated list of category ids
*/
protected function getChildrenCategoriesRecursive(string $idList, $counter = 0): string
{
$result = [];
// add id list to the output
if ($counter === 0) {
$newList = $this->getUidListFromRecords($idList);
if ($newList) {
$result[] = $newList;
}
}
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('sys_category');
$res = $queryBuilder
->select('uid')
->from('sys_category')
->where(
$queryBuilder->expr()->in('parent', $queryBuilder->createNamedParameter(explode(',', $idList), Connection::PARAM_INT_ARRAY))
)
->executeQuery();
while ($row = $res->fetchAssociative()) {
$counter++;
if ($counter > 10000) {
$this->timeTracker->setTSlogMessage('EXT:tt_address: one or more recursive categories where found');
return implode(',', $result);
}
$subcategories = $this->getChildrenCategoriesRecursive((string)$row['uid'], $counter);
$result[] = $row['uid'] . ($subcategories ? ',' . $subcategories : '');
}
$result = implode(',', $result);
return $result;
}
/**
* Fetch ids again from DB to avoid false positives
*
* @param string $idList
* @return string
*/
protected function getUidListFromRecords(string $idList): string
{
$list = [];
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('sys_category');
$rows = $queryBuilder
->select('uid')
->from('sys_category')
->where(
$queryBuilder->expr()->in('uid', $queryBuilder->createNamedParameter(explode(',', $idList), Connection::PARAM_INT_ARRAY))
)
->executeQuery()
->fetchAllAssociative();
foreach ($rows as $row) {
$list[] = $row['uid'];
}
return implode(',', $list);
}
}

View File

@@ -0,0 +1,184 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Service;
use TYPO3\CMS\Core\Cache\CacheManager;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException;
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryHelper;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Service for category related stuff
*
* thanks to https://github.com/b13/t3ext-geocoding for inspiration
*/
class GeocodeService implements SingletonInterface
{
/** @var int */
protected $cacheTime = 7776000;
/** @var string */
protected $geocodingUrl = 'https://maps.googleapis.com/maps/api/geocode/json?language=de&sensor=false';
public function __construct(string $googleMapsKey = '')
{
$this->geocodingUrl .= '&key=' . $googleMapsKey;
}
/**
* geocodes all missing records in a DB table and then stores the values
* in the DB record.
*
* only works if your DB table has the necessary fields
* helpful when calculating a batch of addresses and save the latitude/longitude automatically
*
* @param string $addWhereClause
* @return int
*/
public function calculateCoordinatesForAllRecordsInTable($addWhereClause = ''): int
{
$tableName = 'tt_address';
$latitudeField = 'latitude';
$longitudeField = 'longitude';
$streetField = 'address';
$zipField = 'zip';
$cityField = 'city';
$countryField = 'country';
// Fetch all records without latitude/longitude
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($tableName);
$queryBuilder = $connection->createQueryBuilder();
$queryBuilder->getRestrictions()
->removeAll()
->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$queryBuilder
->select('*')
->from($tableName)
->where(
$queryBuilder->expr()->or(
$queryBuilder->expr()->isNull($latitudeField),
$queryBuilder->expr()->eq($latitudeField, $queryBuilder->createNamedParameter(0, Connection::PARAM_INT)),
$queryBuilder->expr()->eq($latitudeField, 0.00000000000),
$queryBuilder->expr()->isNull($longitudeField),
$queryBuilder->expr()->eq($longitudeField, $queryBuilder->createNamedParameter(0, Connection::PARAM_INT)),
$queryBuilder->expr()->eq($longitudeField, 0.00000000000)
)
)
->setMaxResults(500);
if (!empty($addWhereClause)) {
$queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($addWhereClause));
}
$records = $queryBuilder->executeQuery()->fetchAllAssociative();
if (\count($records) > 0) {
foreach ($records as $record) {
$country = $record[$countryField];
// do the geocoding
if (!empty($record[$zipField]) || !empty($record[$cityField])) {
$coords = $this->getCoordinatesForAddress($record[$streetField], $record[$zipField], $record[$cityField], $country);
if ($coords) {
// Update the record to fill in the latitude and longitude values in the DB
$connection->update(
$tableName,
[
$latitudeField => $coords['latitude'],
$longitudeField => $coords['longitude'],
],
[
'uid' => $record['uid']
]
);
}
}
}
}
return \count($records);
}
/**
* core functionality: asks google for the coordinates of an address
* stores known addresses in a local cache.
*
* @param string $street
* @param string $zip
* @param string $city
* @param string $country
* @return array an array with latitude and longitude
*/
public function getCoordinatesForAddress($street = null, $zip = null, $city = null, $country = ''): array
{
$addressParts = [];
foreach ([$street, $zip . ' ' . $city, $country] as $addressPart) {
if (empty($addressPart)) {
continue;
}
$addressParts[] = trim($addressPart);
}
$address = ltrim(implode(',', $addressParts), ',');
if (empty($address)) {
return [];
}
$cacheObject = $this->initializeCache();
$cacheKey = 'geocode-' . strtolower(str_replace(' ', '-', preg_replace('/[^0-9a-zA-Z ]/m', '', $address)));
// Found in cache? Return it.
if ($cacheObject->has($cacheKey)) {
return $cacheObject->get($cacheKey);
}
$result = $this->getApiCallResult(
$this->geocodingUrl . '&address=' . urlencode($address)
);
if (empty($result) || empty($result['results']) || empty($result['results'][0]['geometry'])) {
return [];
}
$geometry = $result['results'][0]['geometry'];
$result = [
'latitude' => $geometry['location']['lat'],
'longitude' => $geometry['location']['lng'],
];
// Now store the $result in cache and return
$cacheObject->set($cacheKey, $result, [], $this->cacheTime);
return $result;
}
/**
* @param string $url
* @return array
*/
protected function getApiCallResult(string $url): array
{
$response = GeneralUtility::getUrl($url);
$result = json_decode($response, true);
if ($result['status'] !== 'OVER_QUERY_LIMIT') {
return $result;
}
return [];
}
/**
* Initializes the cache for the DB requests.
*
* @param string $name
* @return FrontendInterface Cache Object
*/
protected function initializeCache(string $name = 'ttaddress_geocoding'): FrontendInterface
{
try {
$cacheManager = GeneralUtility::makeInstance(CacheManager::class);
return $cacheManager->getCache($name);
} catch (NoSuchCacheException $e) {
throw new \RuntimeException('Unable to load Cache!', 1548785854);
}
}
}