Is there a good PHP Class to generate a RSS file from mysql tables?
There are ready to use classes for building rss feeds like this one found on phpclasses.org but it is just as easy to read up on the rss spec and generate the XML yourself with XMLWriter which is based on libxml and included in PHP5.
When you are actually generating the feeds yourself it never hurts to understand the spec.
I've wrote few hours ago small classes and tested it with feed validator and it work really fine.
Here source code
<?php
/**
* Top Level Element or RSS Feed 2.0.
*
* RSS Parent Element.
*
* #version 1.0
* #author makemoney2010
* #property array $Channels array of RSSChannell
*/
class RSS
{
public $Channels=array();
function __construct()
{
$this->Channels=array();
}
/**
* Add a new RSSChannell to the Channells
* #method void
* #param RSSChannell $RssChannell
*/
function addChannell($RssChannell)
{
$this->Channels[]=$RssChannell;
}
/**
* Clear all the item within the channells
* #method void
*/
function clearChannells()
{
$this->Channels=array();
}
/**
* Get full RSS xml
* #param boolean $forceCData Define is Cdata must be used or not this value will be propagated within all child RSSchannels and/or their itemChannell
* #return string Full RSS structure that should be used as response or even as string to put within a static file.
*/
function getOutputXML($forceCData)
{
$output='<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">';
foreach($this as $key=>$value)
{
if (is_array($value))
{
foreach($value as $key => $item)
{
/** #var RSSChannell $item */
$output.= $item->getOutputXML($forceCData);
}
}
//$output.=$value->getOutputXML($forceCData);
}
$output.='</rss>';
return $output;
}
}
/**
* Class Channell
* #property string $title Channell title REQUIRED NOT OPTIONAL IN RSS
* #property string $description Description of the channell REQUIRED NOT OPTIONAL IN RSS
* #property string $atomlink <atom:link href="http://dallas.example.com/rss.xml" rel="self" type="application/rss+xml" />
* #property string $copyright Copyright notice for content in the channel: Example {copyright 2002, Spartanburg Herald-Journal} OPTIONAL NOT REQUIRED IN RSS
* #property string $managingEditor Email address for person responsible for editorial content. Example {geo(et)herald.com (George Matesky)} OPTIONAL NOT REQUIRED IN RSS
* #property string $webMaster Email address for person responsible for technical issues relating to channel. Example {betty(et)herald.com (Betty Guernsey)} OPTIONAL NOT REQUIRED IN RSS
* #property string $language Language of the channell OPTIONAL NOT REQUIRED IN RSS
* #property string $pubDate The publication date for the content in the channel. For example, the New York Times publishes on a daily basis, the publication date flips once every 24 hours. That's when the pubDate of the channel changes. All date-times in RSS conform to the Date and Time Specification of RFC 822, with the exception that the year may be expressed with two characters or four characters (four preferred). Example {Sat, 07 Sep 2002 00:00:01 GMT} OPTIONAL NOT REQUIRED IN RSS
* #property string $lastBuildDate The last time the content of the channel changed. Example {Sat, 07 Sep 2002 09:42:31 GMT} OPTIONAL NOT REQUIRED IN RSS
* #property string $category Specify one or more categories that the channel belongs to. Follows the same rules as the <item>-level category element. OPTIONA NOT REQUIRED IN RSS
* #property string $generator A string indicating the program used to generate the channel. Example {MightyInHouse Content System v2.3}
* #property string $docs A URL that points to the documentation for the format used in the RSS file. It's probably a pointer to this page. It's for people who might stumble across an RSS file on a Web server 25 years from now and wonder what it is. More info at http://blogs.law.harvard.edu/tech/rss OPTIONAL NOT REQUIRED IN RSS
* #property string $cloud Allows processes to register with a cloud to be notified of updates to the channel, implementing a lightweight publish-subscribe protocol for RSS feeds. More info here. <cloud domain="rpc.sys.com" port="80" path="/RPC2" registerProcedure="pingMe" protocol="soap"/> OPTIONAL NOT REQUIRED IN RSS
* #property string $ttl ttl stands for time to live. It's a number of minutes that indicates how long a channel can be cached before refreshing from the source. OPTIONAL NOT REQUIRED IN RSS
* #property string $image Specifies a GIF, JPEG or PNG image that can be displayed with the channel OPTIONAL NOT REQUIRED IN RSS
* #property int $rating The PICS rating for the channel OPTIONAL NOT REQUIRED IN RSS
* #property int $skipHours A hint for aggregators telling them which hours they can skip OPTIONAL NOT REQUIRED IN RSS
* #property int $skipDays A hint for aggregators telling them which days they can skip OPTIONAL NOT REQUIRED IN RSS
* #method getCountItems() Get count of items inclued into array ChannellsItems
*/
class RSSChannell{
#region properties
public $language;
private $channellsItems=array();
public $atomlink;
public $title;
public $description;
public $pubDate;
public $copyright;
public $managingEditor;
public $webMaster;
public $lastBuildDate;
public $category;
public $generator;
public $docs;
public $cloud;
public $ttl;
public $image;
public $rating;
public $skipHours;
public $skipDays;
#endregion
#region void
/**
* Summary of __construct
* #param string $lang setup the channell language and the default array of ItemChannell
* #param array $channellsItems as collection of itemChannell
*/
function __construct($lang)
{
$this->language=$lang;
$this->channellsItems=array();
}
/**
* Clear all the items within the $channellsItems array
* #method void clearChannellItems()
*/
function clearChannellItems()
{
$this->channellsItems=array();
}
/**
* Add a new item to the channellsItems collection
* #method void addItemChannell(itemChannell $itemChannell)
* #param ItemChannell $itemChannell
*/
function addItemChannell($itemChannell)
{
$this->channellsItems[]=$itemChannell;
}
/**
* Set basic Email information within the feed about webmaster, copyright,managingEditor at once.If need it could be changed one by one setting its own right value.
* #param mixed $email
* #param mixed $name
*/
function setBasicEmail($email,$name)
{
$this->copyright=$email.' ('.$name.')';
$this->managingEditor=$email.' ('.$name.')';
$this->webMaster=$email.' ('.$name.')';
}
/**
* Set Email information about copyright.
* #param string $email
* #param string $name
* #method void
*/
function setCopyright($email,$name)
{
$this->copyright=$email.' ('.$name.')';
}
/**
* Set Email information about managingEditor
* #param string $email
* #param string $name
* #method void
*/
function setmanagingEditor($email,$name)
{
$this->managingEditor=$email.' ('.$name.')';
}
/**
* Set basic Email information about webmaster
* #param string $email
* #param string $name
* #method void
*/
function setwebMaster($email,$name)
{
$this->webMaster=$email.' ('.$name.')';
}
#endregion
#region functions
/**
* Return the count of all the items within channellsItems
* #return int
*/
function getCountItems()
{
return count($this->channellsItems);
}
/**
* #method function
* #param boolean $forceCData For default True indicate if use CDATA section within string field in order to prevent wrong markup in RSS feed too
*/
function getOutputXML($forceCData=true)
{
$output='<channel>';
$items='';
foreach($this as $key=>$value)
{
if(is_array($value))
{
$o=new ItemChannell();
foreach($value as $item)
{
/** #var ItemChannell $item */
$items.= $item->getOutputXML($forceCData);
}
}else
{
if(!empty($value) || $value !=null || $value!=0 || $value!='')
{
//cheking for atomlink element
if($key==='atomlink')
{
$output.='<atom:link href="'.$value.'" rel="self" type="application/rss+xml" />';
}
else{
if($forceCData)
{
$value=htmlspecialchars($value);
$output.=sprintf('<%1$s><![CDATA[%2$s]]></%1$s>',$key,$value);
}
else
{
$value=htmlspecialchars($value);
$output.=sprintf('<%1$s>%2$s</%1$s>',$key,$value);
}
}
}
}
}
$output.=$items;
$output.='</channel>';
return $output;
}
#endregion
}
/**
* Class ItemChannel exposes all the properties within a fully compliant RSS item element
* #property string $title The title of the item. Example: Venice Film Festival Tries to Quit Sinking
* #property string $link The URL of the item. Example: http://nytimes.com/2004/12/07FEST.html
* #property string $description Example: The item synopsis.Some of the most heated chatter at the Venice Film Festival this week was about the way that the arrival of the stars at the Palazzo del Cinema was being staged.
* #property string $author Email address of the author of the item.
* #property string $category Includes the item in one or more categories.
* #property string $comments URL of a page for comments relating to the item. Example: http://www.myblog.org/cgi-local/mt/mt-comments.cgi?entry_id=290
* #property string $enclosure Describes a media object that is attached to the item.
* #property string $guid A string that uniquely identifies the item.
* #property datetime $pubDate Indicates when the item was published. Example: Sun, 19 May 2002 15:21:36 GMT
* #property string $source The RSS channel that the item came from.
*/
class ItemChannell{
#region properties
public $title;
public $link;
public $description;
public $author;
public $category;
public $comments;
public $enclosure;
public $guid;
public $pubDate;
public $source;
#endregion
#region void
/**
* Constructor of the Items
*/
function __construct()
{
//Nothing required
}
/**
* Set the title of the item
* #method void
* #param string $title
*/
function setTitle($title)
{
$this->title=$title;
}
/**
* Set the link of the item
* #method void
* #param string $link
*/
function setLink($link)
{
$this->link=$link;
}
/**
* Set the description of the item
* #method void
* #param string $description
*/
function setDescription($description)
{
$this->description=$description;
}
/**
* Set the author of the item
* #method void
* #param string $author
*/
function setAuthor($author)
{
$this->author=$author;
}
/**
* Set the category of the item
* #method void
* #param string $category
*/
function setCategory($category)
{
$this->category=$category;
}
/**
* Set comments url of the item
* #method void
* #param mixed $comments
*/
function setCommentsUrl($comments)
{
$this->comments=$comments;
}
/**
* Set enclosure of item
* #method void
* #param string $enclosure
*/
function setEnclosure($enclosure)
{
$this->enclosure=$enclosure;
}
/**
* Set guid of the item
* #method void
* #param string $guid
*/
function setGuidItem($guid)
{
$this->guid=$guid;
}
/**
* Set pubdate of the item
* #method void
* #param datetime $pubDate
*/
function setPubDate($pubDate)
{
$this->pubDate=$pubDate;
}
/**
* Set source of item
* #method void
* #param string $source
*/
function setSource($source)
{
$this->source=$source;
}
#endregion
#region function
/**
* Get the output in xml format for the rss item
* #method function
* #param boolean $forceCDATA Include all the item of type string within a CDATA section in order to prevent issues.Default use it.
* #return string Xml well formatted as requires
*/
function getOutputXML($forceCDATA=true)
{
$output='<item>';
foreach($this as $key=> $value)
{
if(!empty($value) || $value !=null || $value!=0 || $value!='')
{
if($forceCDATA)
{
$value=htmlspecialchars($value);
$output.=sprintf('<%1$s><![CDATA[%2$s]]></%1$s>',$key,$value);
}
else
{
$output.=sprintf('<%1$s>%2$s</%1$s>',$key,$value);
}
}
}
$output.='</item>';
return $output;
}
#endregion
}
With that file you have complete access to all the classes that you could need to create a complete RSS.
Here below a simple example of the use of those classes
function test()
{
//Define a basic RSS object which represent the root ot the RSS Feed too
$rss= new RSS();
//Declare a RSS Channell object and fill the property that you need here below a simple example
$rssChan= new RSSChannell('en');
$rssChan->title='This is my first Channell RSS';
$rssChan->description='this channell is very cool and is built in a simple manner with a specialized class';
$rssChan->category='Category';
$rssChan->setBasicEmail('domain#domain.com','Jhon Doe');
$rssChan->atomlink='http://domain.com/';
$rssChan->link='http://domain.com/';
//Add the channell to the rss root to
$rss->addChannell($rssChan);
//create a simple iteration in order to create specialized list of ItemChannel whith will be used as item in the RSSChannell above
for ($i = 0; $i < 10; $i++)
{
$rssItem= new ItemChannell();
$rssItem->guid='http://domain.com/'.$i;
$rssItem->setCategory('Category names in this fields '.$i);
$rssItem->setAuthor('jhondoe#domain.com (Jhon Doe)');
$rssItem->setDescription('this is a description item within rss');
//Add each item to the RSSChannell collection
$rssChan->addItemChannell($rssItem);
}
//print output into your page don\'t forget that this is an xml output and so you have to set your header as application/xml.
header('Content-Type: application/xml');
//call RSS root Method getOutputXML($bool) (Take a look into description of all methods properties of the class),
echo $rss->getOutputXML(false);
}
That's all you will have a full RSS feed done.
There are more available stuff that could be implemented into the classes but as i need just this have no extened to much this one however you can extend with method to save into a file, append to a file a more other stuff.
I hope this could be a good point to start in order to have a simple way to achieve your goal and make it very easier.
Regards
Makemoney2010
You can use XMLDOM. RSS is just XML.
Related
This question already has answers here:
Reference: Return type of ... should either be compatible with ..., or the #[\ReturnTypeWillChange] attribute should be used
(2 answers)
Closed last year.
i got a problem with PHPIDS on a PHP 8.1 Server.
Here the Errors:
PHP Deprecated: Return type of IDS\Report::count() should either be compatible with Countable::count(): int, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in phpids\ib\IDS\Report.php on line 205
PHP Deprecated: Return type of IDS\Report::getIterator() should either be compatible with IteratorAggregate::getIterator(): Traversable, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in phpids\lib\IDS\Report.php on line 218
I know Deprecated Errors are not that bad, but i want that everything works Perfect.
I Searched in Google several things but no solution found.
Hopefully you find the problem.
i use the actual PHPIDS version from Github (https://github.com/PHPIDS/PHPIDS)
i know this version is not the newest but i didn't found a newer one.
thx for the help
and here the Script Report.php
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* #category Security
* #package PHPIDS
* #author Mario Heiderich <mario.heiderich#gmail.com>
* #author Christian Matthies <ch0012#gmail.com>
* #author Lars Strojny <lars#strojny.net>
* #license http://www.gnu.org/licenses/lgpl.html LGPL
* #link http://php-ids.org/
*/
namespace IDS;
/**
* PHPIDS report object
*
* The report objects collects a number of events and thereby presents the
* detected results. It provides a convenient API to work with the results.
*
* Note that this class implements Countable, IteratorAggregate and
* a __toString() method
*
* #category Security
* #package PHPIDS
* #author Christian Matthies <ch0012#gmail.com>
* #author Mario Heiderich <mario.heiderich#gmail.com>
* #author Lars Strojny <lars#strojny.net>
* #copyright 2007-2009 The PHPIDS Group
* #license http://www.gnu.org/licenses/lgpl.html LGPL
* #link http://php-ids.org/
*/
class Report implements \Countable, \IteratorAggregate
{
/**
* Event container
*
* #var Event[]|array
*/
protected $events = array();
/**
* List of affected tags
*
* This list of tags is collected from the collected event objects on
* demand when IDS_Report->getTags() is called
*
* #var string[]|array
*/
protected $tags = array();
/**
* Impact level
*
* The impact level is calculated on demand by adding the results of the
* event objects on IDS\Report->getImpact()
*
* #var integer
*/
protected $impact = 0;
/**
* Centrifuge data
*
* This variable - initiated as an empty array - carries all information
* about the centrifuge data if available
*
* #var array
*/
protected $centrifuge = array();
/**
* Constructor
*
* #param array $events the events the report should include
*
* #return Report
*/
public function __construct(array $events = null)
{
foreach ((array) $events as $event) {
$this->addEvent($event);
}
}
/**
* Adds an IDS_Event object to the report
*
* #param Event $event IDS_Event
*
* #return self $this
*/
public function addEvent(Event $event)
{
$this->clear();
$this->events[$event->getName()] = $event;
return $this;
}
/**
* Get event by name
*
* In most cases an event is identified by the key of the variable that
* contained maliciously appearing content
*
* #param string|integer $name the event name
*
* #throws \InvalidArgumentException if argument is invalid
* #return Event|null IDS_Event object or false if the event does not exist
*/
public function getEvent($name)
{
if (!is_scalar($name)) {
throw new \InvalidArgumentException('Invalid argument type given');
}
return $this->hasEvent($name) ? $this->events[$name] : null;
}
/**
* Returns list of affected tags
*
* #return string[]|array
*/
public function getTags()
{
if (!$this->tags) {
$this->tags = array();
foreach ($this->events as $event) {
$this->tags = array_merge($this->tags, $event->getTags());
}
$this->tags = array_values(array_unique($this->tags));
}
return $this->tags;
}
/**
* Returns total impact
*
* Each stored IDS_Event object and its IDS_Filter sub-object are called
* to calculate the overall impact level of this request
*
* #return integer
*/
public function getImpact()
{
if (!$this->impact) {
$this->impact = 0;
foreach ($this->events as $event) {
$this->impact += $event->getImpact();
}
}
return $this->impact;
}
/**
* Checks if a specific event with given name exists
*
* #param string|integer $name the event name
*
* #throws \InvalidArgumentException if argument is illegal
* #return boolean
*/
public function hasEvent($name)
{
if (!is_scalar($name)) {
throw new \InvalidArgumentException('Invalid argument given');
}
return isset($this->events[$name]);
}
/**
* Returns total amount of events
*
* #return integer
*/
public function count()
{
return #count($this->events);
}
/**
* Return iterator object
*
* In order to provide the possibility to directly iterate over the
* IDS_Event object the IteratorAggregate is implemented. One can easily
* use foreach() to iterate through all stored IDS_Event objects.
*
* #return \Iterator the event collection
*/
public function getIterator()
{
return new \ArrayIterator($this->events);
}
/**
* Checks if any events are registered
*
* #return boolean
*/
public function isEmpty()
{
return empty($this->events);
}
/**
* Clears calculated/collected values
*
* #return void
*/
protected function clear()
{
$this->impact = 0;
$this->tags = array();
}
/**
* This method returns the centrifuge property or null if not
* filled with data
*
* #return array
*/
public function getCentrifuge()
{
return $this->centrifuge;
}
/**
* This method sets the centrifuge property
*
* #param array $centrifuge the centrifuge data
*
* #throws \InvalidArgumentException if argument is illegal
* #return void
*/
public function setCentrifuge(array $centrifuge = array())
{
if (!$centrifuge) {
throw new \InvalidArgumentException('Empty centrifuge given');
}
$this->centrifuge = $centrifuge;
}
/**
* Directly outputs all available information
*
* #return string
*/
public function __toString()
{
$output = '';
if (!$this->isEmpty()) {
$output .= vsprintf(
"Total impact: %d<br/>\nAffected tags: %s<br/>\n",
array(
$this->getImpact(),
implode(', ', $this->getTags())
)
);
foreach ($this->events as $event) {
$output .= vsprintf(
"<br/>\nVariable: %s | Value: %s<br/>\nImpact: %d | Tags: %s<br/>\n",
array(
htmlspecialchars($event->getName()),
htmlspecialchars($event->getValue()),
$event->getImpact(),
implode(', ', $event->getTags())
)
);
foreach ($event as $filter) {
$output .= vsprintf(
"Description: %s | Tags: %s | ID %s<br/>\n",
array(
$filter->getDescription(),
implode(', ', $filter->getTags()),
$filter->getId()
)
);
}
}
$output .= '<br/>';
if ($centrifuge = $this->getCentrifuge()) {
$output .= vsprintf(
"Centrifuge detection data<br/> Threshold: %s<br/> Ratio: %s",
array(
isset($centrifuge['threshold']) && $centrifuge['threshold'] ? $centrifuge['threshold'] : '---',
isset($centrifuge['ratio']) && $centrifuge['ratio'] ? $centrifuge['ratio'] : '---'
)
);
if (isset($centrifuge['converted'])) {
$output .= '<br/> Converted: ' . $centrifuge['converted'];
}
$output .= "<br/><br/>\n";
}
}
return $output;
}
}
As of PHP 8.1, you have to fix the return type of the functions count() and getIterator() to match with interfaces.
public count(): int (see Countable)
public getIterator(): Traversable (see IteratorAggregate)
class Report implements \Countable, \IteratorAggregate
{
protected array $events;
public function count(): int
{
return count($this->events);
}
public function getIterator(): \Traversable
{
return new \ArrayIterator($this->events);
}
}
so, I have a performance issue with my extbase plugin.
The scenario is such:
I have 4 database tables with data for artists, artworks, exhibitions and publications.
Exhibitions can have artists, artworks and publications as mm relations.
publications and artwork can have artists as mm relations.
In my Model Classes I have those relations as ObjectStorage and use simple findAll() Method for my List View.
So if I get to the Exhibition List View I get every Exhibition and their related Artists and all related Artworks/Publications/Exhibitions of that Artist.
The performance is really bad and a not cached page needs almost a full minute to load.
And this is all because of the heavy data load from the db.
I only need the MM relations on the first level and not further. Is there anyway to config this?
Classes\Domain\Model\Exhibition.php
class Exhibition extends AbstractEntity
{
/**
* #var string
*/
protected $name = '';
/**
* #var string
*/
protected $location = '';
/**
* artists
*
* #var ObjectStorage<\Vendor\Project\Domain\Model\Artist>
* #TYPO3\CMS\Extbase\Annotation\ORM\Lazy
*/
protected $artists = null;
/**
* artworks
*
* #var ObjectStorage<\Vendor\Project\Domain\Model\Artwork>
* #TYPO3\CMS\Extbase\Annotation\ORM\Lazy
*/
protected $artworks;
/**
* publications
*
* #var ObjectStorage<\Vendor\Project\Domain\Model\Publication>
* #TYPO3\CMS\Extbase\Annotation\ORM\Lazy
*/
protected $publications;
/**
* Fal media items
*
* #var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\FileReference>
* #TYPO3\CMS\Extbase\Annotation\ORM\Lazy
*/
protected $falMedia;
public function __construct(string $name = '', string $description = '', string $location = '')
{
$this->setName($name);
$this->setLocation($location);
}
/**
* #param string $name
*/
public function setName(string $name): void
{
$this->name = $name;
}
/**
* #return string
*/
public function getName(): string
{
return $this->name;
}
/**
* #return string
*/
public function getLocation(): string
{
return $this->location;
}
/**
* #param string $location
*/
public function setLocation(string $location): void
{
$this->location = $location;
}
/**
* Returns the artist
*
* #return ObjectStorage<\Vendor\Project\Domain\Model\Artist> $artists
*/
public function getArtists()
{
return $this->artists;
}
/**
* Sets the artist
*
* #param ObjectStorage<\Vendor\GKG\Domain\Model\Artist> $artists
* #return void
*/
public function setArtists(ObjectStorage $artists)
{
$this->artists = $artists;
}
/**
* #param $artworks
*/
public function setArtworks($artworks)
{
$this->artworks = $artworks;
}
/**
* #return ObjectStorage
*/
public function getArtworks()
{
return $this->artworks;
}
/**
* Sets the publications
*
* #param ObjectStorage<\Vendor\Project\Domain\Model\Publication> $oublications
* #return void
*/
public function setPublications(ObjectStorage $publications)
{
$this->publications = $publications;
}
/**
* Returns the publications
*
* #return ObjectStorage<\Vendor\Project\Domain\Model\Publication> $publications
*/
public function getPublications()
{
return $this->publications;
}
/**
* Get the Fal media items
*
* #return \TYPO3\CMS\Extbase\Persistence\ObjectStorage
*/
public function getFalMedia()
{
return $this->falMedia;
}
}
This gets all the Artist data in which all related data is fetched as well (artwork, exhibition and publication). And this is a heavy overload :/
I tried to write my own query and only select the values I need in my frontend:
Classes\Domain\Repository\ExhibitionRepository.php
public function findExhibitions()
{
$languageAspect = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance
(\TYPO3\CMS\Core\Context\Context::class)->getAspect('language');
$sys_language_uid = $languageAspect->getId();
$query = $this->createQuery();
$query->getQuerySettings()->setRespectSysLanguage(true);
$query->statement( "
SELECT DISTINCT e.name, e.uid, e.location, e.fal_media, a.name, a.uid
FROM tx_project_domain_model_exhibition e
LEFT JOIN tx_project_exhibitions_artists_mm
ON tx_project_exhibitions_artists_mm.uid_local = e.uid
LEFT JOIN tx_project_domain_model_artist a
ON tx_project_exhibitions_artists_mm.uid_foreign = a.uid
WHERE e.sys_language_uid = $sys_language_uid
GROUP BY tx_project_exhibitions_artists_mm.uid_local
" );
return $query->execute();
}
But with this approach I’m not able to get the relation data to my assigned view variable inside my Controller. Only the exhibition related stuff.
Classes\Controller\ExhibitionController.php
public function indexAction()
{
$queryResult = $this->exhibitionRepository->findExhibitions();
$this->view->assign('exhibitions', $queryResult);
}
Any insight and advice on how to tackle this problem and only get the needed Data and not everything from the ObjectStorage?
Thanks in advance and best regards
It looks like your query cost is very high with all the JOINS and without a condition on a indexed column.
You could check it with an EXPLAIN PLAN(MySQL) / EXECUTION PLAN(MS).
Set condition with an index to boost Performance
Rather using the Comparing operations leads to Performance.
https://docs.typo3.org/m/typo3/book-extbasefluid/main/en-us/6-Persistence/3-implement-individual-database-queries.html
Another performance boost is to use Ajax, just search for "TYPO3 Ajax":
get an identifier and display it in the Frontend:
SELECT DISTINCT e. uid, e.name FROM tx_project_domain_model_exhibition
On identifier click (name or whatever) make a call of the AjaxController.php:
...
$this->exhibitionRepository->findByUid($uid);
...
or make your own repository comparing
...
$query->matching(
$query->logicalAnd(
$query->equals('uid', $uid),
$query->equals('name', $name)
)
);
...
show the data on a Modal Window,Accordion or a new View.
I am trying to mention a property of my class somewhere else in other comments of my class, ie. in a method of that class.
For example if we have this code:
(please search for: property $mention -- #property Village::mention does not work)
class Village {
/**
* #var array Data container.
*/
public $data = [];
/**
*
*/
public $mention = 'Me';
/**
* Village constructor which injects data.
*
* #param $data
*/
public function __construct($data) {
$this->data = $data;
}
/**
* A factory for our Villages.
*
* #return Village
*/
public static function hillbilly() {
return new Village;
}
/**
* Name tells the story...
*
* Now somewhere at this exact point I want to mention the
* $mention property -- #property Village::mention does not work
* nor does #property $mention either...
*
* #return array Rednecks unset.
*/
public function redneck() {
if(sizeof($data)) {
unset($data);
}
return $data;
}
}
$countryside = [
'important' => 'data',
'axe' => 'knifes',
'shovel' => 'hoe',
'trowel' => 'mixer',
];
$village = Village::hillbilly($countryside);
How do I make a mention of a property in PHPDoc?
If you need to have the $mention in the docblock text, one would usually use the inline see {#see element description}:
/**
* Name tells the story...
*
* Now somewhere at this exact point I want to mention the
* {#see Village::$mention} property.
*
* #return array Rednecks unset.
* #see Village::$mention
* #uses Village::$mention
*/
public function redneck() {
if(sizeof($data)) {
unset($data);
}
return $data;
}
The #see or #uses standalone tags are also available, but not for embedding the link into the docblock narrative text.
Note that older phpDocumentor only allowed the inlink link tag {#link url|element description}.
There has been a solution for template variants, which allowed to set a suffix for a fluid template file used by an extbase controller. It was created by Peter Niederlag and was improved by Bastian Waidelich.
The solution is not working any longer in TYPO3 8.7 because the code has been refactored and the method expandGenericPathPattern in TemplateView does not exist anymore.
How should I implement such variant view in TYPO3 8.7?
$this->view->getRenderingContext()->setControllerAction('MyAction.Variant'); should do the trick (from any initializeAction method or within action method). Note that contrary to the view override class you linked to, this approach means you must have the original action template in the path.
I created the following classes in my extension, which implement the VariantView for TYPO3 8.7.
Classes\View\VariantView.php
<?php
namespace Vendor\Extkey\View;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Fluid\View\TemplateView;
/**
* Extended Fluid Template View that supports different "variants"
*
* #license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
*/
class VariantView extends TemplateView
{
/**
* #param string $layoutVariant
* #return void
*/
public function setLayoutVariant($layoutVariant)
{
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
$this->baseRenderingContext->setTemplatePaths($objectManager->get(TemplatePaths::class));
/** #var TemplatePaths $templatePaths */
$templatePaths = $this->baseRenderingContext->getTemplatePaths();
$templatePaths->setLayoutVariant($layoutVariant);
}
const DEFAULT_LAYOUT_VARIANT = '.default';
}
Classes\View\TemplatePaths.php
<?php
namespace Vendor\Extkey\View;
class TemplatePaths extends \TYPO3\CMS\Fluid\View\TemplatePaths
{
/**
* Layout variant to use for this view.
*
* #var string
*/
protected $layoutVariant = VariantView::DEFAULT_LAYOUT_VARIANT;
/**
* #param string $layoutVariant
* #return void
*/
public function setLayoutVariant($layoutVariant)
{
$this->layoutVariant = $layoutVariant;
}
/**
* Wrapper for parent class method which adds layout variant in action parameter
*
* #param string $controller
* #param string $action
* #param string $format
* #return string|NULL
* #api
*/
public function resolveTemplateFileForControllerAndActionAndFormat($controller, $action, $format = self::DEFAULT_FORMAT)
{
$action = $action . $this->layoutVariant;
return parent::resolveTemplateFileForControllerAndActionAndFormat($controller, $action, $format = self::DEFAULT_FORMAT);
}
}
In your controller add the following lines:
protected function setViewConfiguration(\TYPO3\CMS\Extbase\Mvc\View\ViewInterface $view) {
parent::setViewConfiguration($view);
$view->setLayoutVariant($this->settings['layoutVariant']);
}
I am trying to figure out if it is possible to use PHPdoc to define the object properties being returned by a function or a object method.
Say I have the following class:
class SomeClass {
public function staffDetails($id){
$object = new stdClass();
$object->type = "person";
$object->name = "dave";
$object->age = "46";
return $object;
}
}
Now, it is easy enough to define input parameters.
/**
* Get Staff Member Details
*
* #param string $id staff id number
*
* #return object
*/
class SomeClass {
public function staffDetails($id){
$object = new stdClass();
$object->type = "person";
$object->name = "dave";
$object->age = "46";
return $object;
}
}
The question is is there a similar thing for defining properties of the output object (of a stdClass) returned by the method in question. So that another programmer does not have to open this class and manually look into the method to see what the return object is returning?
Here it is 4 years later, and there still does not appear to be a way to annotate the properties of a stdClass object as originally described in your question.
Collections had been proposed in PSR-5, but that appears to have been shot down: https://github.com/php-fig/fig-standards/blob/211063eed7f4d9b4514b728d7b1810d9b3379dd1/proposed/phpdoc.md#collections
It seems there are only two options available:
Option 1:
Create a normal class representing your data object and annotate the properties.
class MyData
{
/**
* This is the name attribute.
* #var string
*/
public $name;
/**
* This is the age attribute.
* #var integer
*/
public $age;
}
Option 2:
Create a generic Struct type class as suggested by Gordon and extend it as your data object, using the #property annotation to define what generic values are possible to access with __get and __set.
class Struct
{
/**
* Private internal struct attributes
* #var array
*/
private $attributes = [];
/**
* Set a value
* #param string $key
* #param mixed $value
*/
public function __set($key, $value)
{
$this->attributes[$key] = $value;
}
/**
* Get a value
* #param string $key
* #return mixed
*/
public function __get($key)
{
return isset($this->attributes[$key]) ? $this->attributes[$key] : null;
}
/**
* Check if a key is set
* #param string $key
* #return boolean
*/
public function __isset($key)
{
return isset($this->attributes[$key]) ? true : false;
}
}
/**
* #property string $name
* #property integer $age
*/
class MyData extends Struct
{
// Can optionally add data mutators or utility methods here
}
You have only two way to document the structure of the result class.
1.One can describe the structure in a comment text. For example:
class SomeClass
{
/**
* Getting staff detail.
* Result object has following structure:
* <code>
* $type - person type
* $name - person name
* $age - person age
* </code>
* #param string $id staff id number
*
* #return stdClass
*
*/
public function staffDetails($id){
$object = new stdClass();
$object->type = "person";
$object->name = "dave";
$object->age = "46";
return $object;
}
}
2.One can create a data type that will inheritance stdClass and it will have an annotation of a result object. For example:
/**
* #property string $type Person type
* #property string $name Person name
* #property integer $age Person age
*/
class DTO extends stdClass
{}
And use it in your other classes
class SomeClass {
/**
* Getting staff detail.
*
* #param string $id staff id number
*
* #return DTO
*
*/
public function staffDetails($id){
$object = new DTO();
$object->type = "person";
$object->name = "dave";
$object->age = "46";
return $object;
}
}
In my opinion, this way is better than a description in the text comment because it makes the code more obvious
If you are using PHP 7, you can define anonymous class.
class SomeClass {
public function staffDetails($id){
$object = (new class() extends stdClass {
public /** #var string */ $type;
public /** #var string */ $name;
public /** #var int */ $age;
});
$object->type = "person";
$object->name = "dave";
$object->age = 46;
return $object;
}
}
It is working for my IDE (tested in NetBeans)
With for example json_decode it's harder to use own classes instead of stdClass, but in my case I just created dummy file with class definitions, which really isn't loaded and I'm adding own classes as #return (works for intelephense on vscode).
PHPdocObjects.php
/**
* class only for PHPdoc (do not include)
*/
class Member {
/** #var string */
public $type;
/** #var string */
public $name;
/** #var string */
public $age;
}
/**
* Other format
*
* #property string $type;
* #property string $name;
* #property string $age;
*/
class MemberAlt {}
SomeClass.php
/**
* Get Staff Member Details
*
* #param string $id staff id number
*
* #return Member I'm in fact stdClass
*/
class SomeClass {
public function staffDetails($id){
$object = json_decode('{"type":"person","name":"dave","age":"46"}');
return $object;
}
}
The hack I use for autocomplete in PhpStorm:
Create some meta file which will contain some classes to describe your structures. The file is never included and structures have their own name rules in order not to mess them with real existing classes:
<?php
/*
meta.php
never included
*/
/**
* #property string $type
* #property string $name
* #property string $age
*/
class StaffDetails_meta {}
Use the meta class as a return value in your real code PHPDoc:
<?php
/*
SomeClass.php
eventually included
*/
class SomeClass
{
/**
* Get Staff Member Details
*
* #param string $id staff id number
*
* #return StaffDetails_meta
*/
public function staffDetails($id)
{
$object = new stdClass();
$object->type = "person";
$object->name = "dave";
$object->age = "46";
return $object;
}
}
Congratulations, this will make your IDE autocomplete your code when you'd typing something like (new SomeClass)->staffDetails('staff_id')->
P.S.: I know, almost 10 years passed but still actual