Search

For a new site I'm going to start I want to rate terraces.

For every terrace the Increment Number counts how many times the terrace has been shown.
Every time someone votes the terrace an event counts this and stores the value inside a field.
And also when someone post a comment an event counts this and stores the value inside a field.

The issue where I'm dealing with is that I want to calculate which terrace is the most popular. I want to count the pageviews 1 time, the vote 2 times and the comment 3 times, like this:

pageview + vote + vote + comment + comment + comment = popular rate

When a terrace has 34 pageviews, 8 votes and 3 comment this will be the "popular rate":

34 + 8 + 8 + 3 + 3 + 3 = 59

I want to show on the homepage of my website the 3 most popular terraces.

Has someone a suggestion to calculate and sort this?

I think you want to look into utilizing template recursion via your XSLT. Here's the basic idea:

<xsl:call-template name="calculate-sum">
    <xsl:with-param name="path" select="/path/to/data"/>
</xsl:call-template>

<xsl:choose>
    <xsl:when test="$position &lt; $entry-count">
      <xsl:call-template name="calculate-sum">
          <xsl:with-param name="path" select="$path"/>
          <xsl:with-param name="position" select="$position + 1"/>
          <xsl:with-param name="tally" select="$tally + entry[$position]/data/to/add"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$tally" />
    </xsl:otherwise>
</xsl:choose>

Lewis, can you explain your idea, because I do not see the way how you display the 3 terraces with the highest popular rate.

You can do that math using the $tally parameter. Can you post your XML.

Hmmm... sorting after may present an issue with this approach.

@whgdesign - do you have any XML that you can post?

Please mind that doing all that in the XML won't scale well. You'd have to select all entries from the terraces section, all comments, all votes, all pageviews; then do the calculations and clear out entries that you don't need (lower scores, pagination etc.).

I imagine your terraces section becoming quite big over time (otherwise you wouldn't need the rating system in the first place, right?).

You could do the rating in a cron job: A script that selects all the data (entries, comments, votes etc.), calculates the values and updates a "score" field in your terraces section.

You could do high-profile candidates more often: update the top 10 every minute, do the top 250 every 10 minutes and the rest every hour.

Can you combine these into an <xsl:sort ... /> element?

<xsl:sort select="sum(pageview, (vote * 2), (comment * 3))" data-type="number" />

I haven't tried it, but it might work.

However sorting in your XSLT means you can't use data source pagination, and means you have to return all entries. If you want to use data source pagination, then you will need to use the Reflection field to calculate the number, and force the field to update when the increment number fields do their thing.

Every time someone votes the terrace an event counts this and stores the value inside a field. And also when someone post a comment an event counts this and stores the value inside a field.

Are these custom events? If so, after incrementing the value you could use the EntryManager class to get your entry (an Entry object), then call its commit() method. This should have the same effect as clicking "Save changes" in the Symphony backend, so the Reflection field should update its value.

Another option is to have a scheduled cron task that does this for you, if you don't need the total to be accurate all the time (i.e. there would be a delay before the value gets updated).

@nickdunn - The events are events using the EventEx extension. So when someone votes or post a comment the comment/votes section is updated and also the terraces section. This is a part of the <form> element which update the entries inside the terrace section.

<input name="terrace[system:id]" type="hidden"><xsl:attribute name="value"><xsl:value-of select="@id"/></xsl:attribute></input>

<input name="terrace[votes]" type="hidden"><xsl:attribute name="value"><xsl:if test="votes != '' and votes != 'None'"><xsl:value-of select="votes/text() + '1'"/></xsl:if><xsl:if test="not(votes) or votes='None'">1</xsl:if></xsl:attribute></input>

<input name="terrace[popularrate]" type="hidden"><xsl:attribute name="value"><xsl:if test="popularrate != ''"><xsl:value-of select="popularrate/text() + '2'"/></xsl:if><xsl:if test="not(popularrate)">1</xsl:if></xsl:attribute></input>

I think the best solution is to update the reflection field every time a terrace is shown. The code of the field.incrementnumber.php is:

<?php

    require_once(EXTENSIONS . '/numberfield/fields/field.number.php');

    Class fieldIncrementNumber extends fieldNumber {

        function __construct(&$parent){
            parent::__construct($parent);
            $this->_name = 'Increment Number';
            $this->_required = TRUE;
        }

        function canToggle(){
            return TRUE;
        }

        function getToggleStates(){
            return array('0' => __('Reset to 0'));
        }

        public function fetchIncludableElements(){
            return array(
                $this->get('element_name') . ': value only',
                $this->get('element_name') . ': increment'
            );
        }

        function toggleFieldData($data, $value){
            $data['value'] = $value;
            return $data;
        }

        function appendFormattedElement(&$wrapper, $data, $encode=FALSE, $mode=NULL, $entry_id=NULL){
            if(!is_array($data) || empty($data)) return;

            $value = (int) $data['value'];

            if($mode == NULL) $mode = 'increment';

            if (Symphony::Engine() instanceof Frontend && $mode == 'increment') {
                $value = ++$value;
                $entry_id = $wrapper->getAttribute('id');
                Symphony::Database()->update(
                    array('value' => $value),
                    "sym_entries_data_{$this->_fields['id']}",
                    "entry_id={$entry_id}"
                );
            }

            $increment_number = new XMLElement($this->get('element_name'), $value);
            $wrapper->appendChild($increment_number);
        }

        function displayPublishPanel(&$wrapper, $data=NULL, $flagWithError=NULL, $fieldnamePrefix=NULL, $fieldnamePostfix=NULL){
            $value = $data['value'];        
            $label = Widget::Label($this->get('label'));

            if($this->get('required') != 'yes') $label->appendChild(new XMLElement('i', 'Optional'));
            $label->appendChild(Widget::Input('fields'.$fieldnamePrefix.'['.$this->get('element_name').']'.$fieldnamePostfix, (strlen($value) != 0 ? $value : 0)));

            if($flagWithError != NULL) {
                $wrapper->appendChild(Widget::wrapFormElementWithError($label, $flagWithError));
            } else {
                $wrapper->appendChild($label);
            }
        }

    }

I need to change this PHP to update the field popularrate so the script calculate +1 to it's own value.

I don't know how to adjust the PHP of the Incrementnumber extension so he also update the popular rate field.
Can someone help me with this?

Create an account or sign in to comment.

Symphony • Open Source XSLT CMS

Server Requirements

  • PHP 5.3-5.6 or 7.0-7.3
  • PHP's LibXML module, with the XSLT extension enabled (--with-xsl)
  • MySQL 5.5 or above
  • An Apache or Litespeed webserver
  • Apache's mod_rewrite module or equivalent

Compatible Hosts

Sign in

Login details