443 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			443 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
 | 
						|
/*
 | 
						|
 * This file is part of the package thucke/th-rating.
 | 
						|
 *
 | 
						|
 * For the full copyright and license information, please read the
 | 
						|
 * LICENSE file that was distributed with this source code.
 | 
						|
 */
 | 
						|
 | 
						|
namespace WapplerSystems\BookmarksLikesRatings\Service;
 | 
						|
 | 
						|
use TYPO3\CMS\Core\Database\ConnectionPool;
 | 
						|
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
 | 
						|
use TYPO3\CMS\Core\Log\LogLevel;
 | 
						|
use TYPO3\CMS\Core\Utility\GeneralUtility;
 | 
						|
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
 | 
						|
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
 | 
						|
 | 
						|
/**
 | 
						|
 * The voter
 | 
						|
 */
 | 
						|
class RichSnippetService extends AbstractExtensionService
 | 
						|
{
 | 
						|
    /**
 | 
						|
     * Instances of AggregateRating may appear as properties of the following types
 | 
						|
     * This list derives from Google's information about supported types in aggregateRating (20 May 2021)
 | 
						|
     * (see https://developers.google.com/search/docs/data-types/review-snippet#aggregated-rating-type-definition)
 | 
						|
     * @const array
 | 
						|
     */
 | 
						|
    protected const VALID_AGGREGATE_RATING_SCHEMA_TYPES = [
 | 
						|
        'Book',
 | 
						|
        'Audiobook',
 | 
						|
        'Course',
 | 
						|
        'CreativeWorkSeason',
 | 
						|
        'PodcastSeason',
 | 
						|
        'RadioSeason',
 | 
						|
        'TVSeason',
 | 
						|
        'CreativeWorkSeries',
 | 
						|
        'BookSeries',
 | 
						|
        'MovieSeries',
 | 
						|
        'Periodical',
 | 
						|
        'ComicSeries',
 | 
						|
        'Newspaper',
 | 
						|
        'PodcastSeries',
 | 
						|
        'RadioSeries',
 | 
						|
        'TVSeries',
 | 
						|
        'VideoGameSeries',
 | 
						|
        'Episode',
 | 
						|
        'PodcastEpisode',
 | 
						|
        'RadioEpisode',
 | 
						|
        'TVEpisode',
 | 
						|
        'Event',
 | 
						|
        'BusinessEvent',
 | 
						|
        'ChildrensEvent',
 | 
						|
        'ComedyEvent',
 | 
						|
        'CourseInstance',
 | 
						|
        'DanceEvent',
 | 
						|
        'DeliveryEvent',
 | 
						|
        'EducationEvent',
 | 
						|
        'EventSeries',
 | 
						|
        'ExhibitionEvent',
 | 
						|
        'Festival',
 | 
						|
        'FoodEvent',
 | 
						|
        'Hackathon',
 | 
						|
        'LiteraryEvent',
 | 
						|
        'MusicEvent',
 | 
						|
        'PublicationEvent',
 | 
						|
        'BroadcastEvent',
 | 
						|
        'OnDemandEvent',
 | 
						|
        'SaleEvent',
 | 
						|
        'ScreeningEvent',
 | 
						|
        'SocialEvent',
 | 
						|
        'SportsEvent',
 | 
						|
        'TheaterEvent',
 | 
						|
        'VisualArtsEvent',
 | 
						|
        'Game',
 | 
						|
        'VideoGame',
 | 
						|
        'HowTo',
 | 
						|
        'Recipe',
 | 
						|
        'AnimalShelter',
 | 
						|
        'ArchiveOrganization',
 | 
						|
        'AutomotiveBusiness',
 | 
						|
        'AutoBodyShop',
 | 
						|
        'AutoDealer',
 | 
						|
        'AutoRental',
 | 
						|
        'AutoRepair',
 | 
						|
        'AutoWash',
 | 
						|
        'GasStation',
 | 
						|
        'MotorcycleDealer',
 | 
						|
        'MotorcycleRepair',
 | 
						|
        'ChildCare',
 | 
						|
        'DryCleaningOrLaundry',
 | 
						|
        'EmergencyService',
 | 
						|
        'FireStation',
 | 
						|
        'Hospital',
 | 
						|
        'PoliceStation',
 | 
						|
        'EmploymentAgency',
 | 
						|
        'EntertainmentBusiness',
 | 
						|
        'AdultEntertainment',
 | 
						|
        'AmusementPark',
 | 
						|
        'ArtGallery',
 | 
						|
        'Casino',
 | 
						|
        'ComedyClub',
 | 
						|
        'MovieTheater',
 | 
						|
        'NightClub',
 | 
						|
        'FinancialService',
 | 
						|
        'AccountingService',
 | 
						|
        'AutomatedTeller',
 | 
						|
        'BankOrCreditUnion',
 | 
						|
        'InsuranceAgency',
 | 
						|
        'FoodEstablishment',
 | 
						|
        'Bakery',
 | 
						|
        'BarOrPub',
 | 
						|
        'Brewery',
 | 
						|
        'CafeOrCoffeeShop',
 | 
						|
        'Distillery',
 | 
						|
        'FastFoodRestaurant',
 | 
						|
        'IceCreamShop',
 | 
						|
        'Restaurant',
 | 
						|
        'Winery',
 | 
						|
        'GovernmentOffice',
 | 
						|
        'PostOffice',
 | 
						|
        'HealthAndBeautyBusiness',
 | 
						|
        'BeautySalon',
 | 
						|
        'DaySpa',
 | 
						|
        'HairSalon',
 | 
						|
        'NailSalon',
 | 
						|
        'TattooParlor',
 | 
						|
        'HomeAndConstructionBusiness',
 | 
						|
        'Electrician',
 | 
						|
        'GeneralContractor',
 | 
						|
        'HVACBusiness',
 | 
						|
        'HousePainter',
 | 
						|
        'Locksmith',
 | 
						|
        'MovingCompany',
 | 
						|
        'Plumber',
 | 
						|
        'RoofingContractor',
 | 
						|
        'InternetCafe',
 | 
						|
        'LegalService',
 | 
						|
        'Attorney',
 | 
						|
        'Notary',
 | 
						|
        'Library',
 | 
						|
        'LodgingBusiness',
 | 
						|
        'BedAndBreakfast',
 | 
						|
        'Campground',
 | 
						|
        'Hostel',
 | 
						|
        'Hotel',
 | 
						|
        'Motel',
 | 
						|
        'Resort',
 | 
						|
        'MedicalBusiness',
 | 
						|
        'CovidTestingFacility',
 | 
						|
        'Optician',
 | 
						|
        'Pharmacy',
 | 
						|
        'Physician',
 | 
						|
        'ProfessionalService',
 | 
						|
        'RadioStation',
 | 
						|
        'RealEstateAgent',
 | 
						|
        'RecyclingCenter',
 | 
						|
        'SelfStorage',
 | 
						|
        'ShoppingCenter',
 | 
						|
        'SportsActivityLocation',
 | 
						|
        'BowlingAlley',
 | 
						|
        'ExerciseGym',
 | 
						|
        'GolfCourse',
 | 
						|
        'HealthClub',
 | 
						|
        'PublicSwimmingPool',
 | 
						|
        'SkiResort',
 | 
						|
        'SportsClub',
 | 
						|
        'StadiumOrArena',
 | 
						|
        'TennisComplex',
 | 
						|
        'Store',
 | 
						|
        'AutoPartsStore',
 | 
						|
        'BikeStore',
 | 
						|
        'BookStore',
 | 
						|
        'ClothingStore',
 | 
						|
        'ComputerStore',
 | 
						|
        'ConvenienceStore',
 | 
						|
        'DepartmentStore',
 | 
						|
        'ElectronicsStore',
 | 
						|
        'Florist',
 | 
						|
        'FurnitureStore',
 | 
						|
        'GardenStore',
 | 
						|
        'GroceryStore',
 | 
						|
        'HardwareStore',
 | 
						|
        'HobbyShop',
 | 
						|
        'HomeGoodsStore',
 | 
						|
        'JewelryStore',
 | 
						|
        'LiquorStore',
 | 
						|
        'MensClothingStore',
 | 
						|
        'MobilePhoneStore',
 | 
						|
        'MovieRentalStore',
 | 
						|
        'MusicStore',
 | 
						|
        'OfficeEquipmentStore',
 | 
						|
        'OutletStore',
 | 
						|
        'PawnShop',
 | 
						|
        'PetStore',
 | 
						|
        'ShoeStore',
 | 
						|
        'SportingGoodsStore',
 | 
						|
        'TireShop',
 | 
						|
        'ToyStore',
 | 
						|
        'WholesaleStore',
 | 
						|
        'TelevisionStation',
 | 
						|
        'TouristInformationCenter',
 | 
						|
        'TravelAgency',
 | 
						|
        'MediaObject',
 | 
						|
        '3DModel',
 | 
						|
        'AudioObject',
 | 
						|
        'DataDownload',
 | 
						|
        'ImageObject',
 | 
						|
        'Barcode',
 | 
						|
        'LegislationObject',
 | 
						|
        'MusicVideoObject',
 | 
						|
        'VideoObject',
 | 
						|
        'Movie',
 | 
						|
        'MusicPlaylist',
 | 
						|
        'MusicAlbum',
 | 
						|
        'MusicRelease',
 | 
						|
        'MusicRecording',
 | 
						|
        'Organization',
 | 
						|
        'Airline',
 | 
						|
        'Consortium',
 | 
						|
        'Corporation',
 | 
						|
        'EducationalOrganization',
 | 
						|
        'CollegeOrUniversity',
 | 
						|
        'ElementarySchool',
 | 
						|
        'HighSchool',
 | 
						|
        'MiddleSchool',
 | 
						|
        'Preschool',
 | 
						|
        'School',
 | 
						|
        'FundingScheme',
 | 
						|
        'GovernmentOrganization',
 | 
						|
        'LibrarySystem',
 | 
						|
        'LocalBusiness',
 | 
						|
        'MedicalOrganization',
 | 
						|
        'NGO',
 | 
						|
        'NewsMediaOrganization',
 | 
						|
        'Dentist',
 | 
						|
        'DiagnosticLab',
 | 
						|
        'MedicalClinic',
 | 
						|
        'VeterinaryCare',
 | 
						|
        'PerformingGroup',
 | 
						|
        'DanceGroup',
 | 
						|
        'MusicGroup',
 | 
						|
        'TheaterGroup',
 | 
						|
        'Project',
 | 
						|
        'FundingAgency',
 | 
						|
        'ResearchProject',
 | 
						|
        'SportsOrganization',
 | 
						|
        'SportsTeam',
 | 
						|
        'WorkersUnion',
 | 
						|
        'Product',
 | 
						|
        'IndividualProduct',
 | 
						|
        'ProductCollection',
 | 
						|
        'ProductGroup',
 | 
						|
        'ProductModel',
 | 
						|
        'SomeProducts',
 | 
						|
        'Vehicle',
 | 
						|
        'BusOrCoach',
 | 
						|
        'Car',
 | 
						|
        'Motorcycle',
 | 
						|
        'MotorizedBicycle',
 | 
						|
        'SoftwareApplication',
 | 
						|
        'MobileApplication',
 | 
						|
        'WebApplication'
 | 
						|
    ];
 | 
						|
 | 
						|
    /**
 | 
						|
     * @var string
 | 
						|
     */
 | 
						|
    protected $schema = 'Product';
 | 
						|
 | 
						|
    /**
 | 
						|
     * @var string
 | 
						|
     */
 | 
						|
    protected $anchor;
 | 
						|
 | 
						|
    /**
 | 
						|
     * @var string
 | 
						|
     */
 | 
						|
    protected $name;
 | 
						|
 | 
						|
    /**
 | 
						|
     * @var string
 | 
						|
     */
 | 
						|
    protected $description;
 | 
						|
 | 
						|
    /**
 | 
						|
     * @var array
 | 
						|
     */
 | 
						|
    protected $richSnippetConfig;
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param array $settings
 | 
						|
     * @return bool
 | 
						|
     */
 | 
						|
    public function setRichSnippetConfig(array $settings): bool
 | 
						|
    {
 | 
						|
        $this->logger->log(LogLevel::DEBUG, 'setRichSnippetConfig Entry point', $settings);
 | 
						|
        $this->richSnippetConfig['tablename'] = $settings['ratetable'];
 | 
						|
        $this->richSnippetConfig['richSnippetFields'] = $settings['richSnippetFields'];
 | 
						|
 | 
						|
        if (is_array($this->richSnippetConfig['richSnippetFields'])) {
 | 
						|
            $this->logger->log(
 | 
						|
                LogLevel::DEBUG,
 | 
						|
                'setRichSnippetConfig Exit point',
 | 
						|
                $this->richSnippetConfig['richSnippetFields']
 | 
						|
            );
 | 
						|
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
 | 
						|
        $this->logger->log(LogLevel::DEBUG, 'setRichSnippetConfig Exit point');
 | 
						|
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @return string|false
 | 
						|
     */
 | 
						|
    public function getRichSnippetConfig()
 | 
						|
    {
 | 
						|
        return GeneralUtility::makeInstance(\WapplerSystems\BookmarksLikesRatings\Service\JsonService::class)
 | 
						|
            ->encodeToJson($this->richSnippetConfig);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @return string
 | 
						|
     */
 | 
						|
    public function getSchema(): string
 | 
						|
    {
 | 
						|
        return $this->schema;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param string|null $schema
 | 
						|
     * @throws \WapplerSystems\BookmarksLikesRatings\Exception\InvalidAggregateRatingSchemaTypeException if parameter is invalid
 | 
						|
     */
 | 
						|
    public function setSchema(?string $schema): void
 | 
						|
    {
 | 
						|
        if (!empty($schema)) {
 | 
						|
            if (in_array($schema, self::VALID_AGGREGATE_RATING_SCHEMA_TYPES, true)) {
 | 
						|
                $this->schema = $schema;
 | 
						|
            } else {
 | 
						|
                throw new \WapplerSystems\BookmarksLikesRatings\Exception\InvalidAggregateRatingSchemaTypeException(
 | 
						|
                    LocalizationUtility::translate(
 | 
						|
                        'error.richSnippetConfiguration.AggregateRatingPropertySchema',
 | 
						|
                        'ThRating'
 | 
						|
                    ),
 | 
						|
                    1521487362
 | 
						|
                );
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @return string
 | 
						|
     */
 | 
						|
    public function getAnchor(): string
 | 
						|
    {
 | 
						|
        return $this->anchor;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param string $anchor
 | 
						|
     */
 | 
						|
    public function setAnchor(string $anchor): void
 | 
						|
    {
 | 
						|
        $this->anchor = $anchor;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param string|null $name
 | 
						|
     */
 | 
						|
    public function setName(?string $name): void
 | 
						|
    {
 | 
						|
        $this->name = $name;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @return string|null
 | 
						|
     */
 | 
						|
    public function getName(): ?string
 | 
						|
    {
 | 
						|
        return $this->name;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @return string|null
 | 
						|
     */
 | 
						|
    public function getDescription(): ?string
 | 
						|
    {
 | 
						|
        return $this->description;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param string|null $description
 | 
						|
     */
 | 
						|
    public function setDescription(?string $description): void
 | 
						|
    {
 | 
						|
        $this->description = $description;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param int $uid
 | 
						|
     * @return RichSnippetService
 | 
						|
     *@throws \WapplerSystems\BookmarksLikesRatings\Exception\InvalidAggregateRatingSchemaTypeException
 | 
						|
     */
 | 
						|
    public function getRichSnippetObject(int $uid): self
 | 
						|
    {
 | 
						|
        $this->logger->log(LogLevel::DEBUG, 'getRichSnippetObject Entry point');
 | 
						|
        $this->setSchema($this->richSnippetConfig['richSnippetFields']['aggregateRatingSchemaType']);
 | 
						|
        if (empty($this->richSnippetConfig['richSnippetFields']['name'])) {
 | 
						|
            $this->logger->log(LogLevel::INFO, 'No name field defined - skipping database access');
 | 
						|
            unset($this->name, $this->description);
 | 
						|
            throw new \TYPO3\CMS\Core\Exception();
 | 
						|
        } else {
 | 
						|
            /** @var QueryBuilder $queryBuilder */
 | 
						|
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
 | 
						|
                ->getQueryBuilderForTable($this->richSnippetConfig['tablename']);
 | 
						|
 | 
						|
            //fetch whole row from database
 | 
						|
            /** @var array $row */
 | 
						|
            $row = $queryBuilder
 | 
						|
                ->select('*')
 | 
						|
                ->from($this->richSnippetConfig['tablename'])
 | 
						|
                ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid)))
 | 
						|
                ->execute()
 | 
						|
                ->fetch();
 | 
						|
 | 
						|
            $this->logger->log(LogLevel::DEBUG, 'Data fetched', $row);
 | 
						|
            $this->setName($row[$this->richSnippetConfig['richSnippetFields']['name']]);
 | 
						|
 | 
						|
            $contentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class);
 | 
						|
            $this->setDescription($row[$this->richSnippetConfig['richSnippetFields']['description']]);
 | 
						|
            //$this->setDescription($contentObjectRenderer->cObjGetSingle('TEXT', $this->richSnippetConfig['richSnippetFields']['description']));
 | 
						|
            //$this->setDescription($contentObjectRenderer->render($this->richSnippetConfig['richSnippetFields']['description']));
 | 
						|
        }
 | 
						|
        $this->logger->log(LogLevel::DEBUG, 'getRichSnippetObject Exit point', (array)$this);
 | 
						|
 | 
						|
        return $this;
 | 
						|
    }
 | 
						|
}
 |