Joomla Parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM [duplicate] - php

This question already has answers here:
PHP expects T_PAAMAYIM_NEKUDOTAYIM?
(11 answers)
Closed 9 years ago.
I have building a web site but i have a error code and i couldnt fould a fix it
Error
Parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM in /home/eneskura/public_html/administrator/components/com_tz_portfolio/helpers/tz_portfolio.php on line 42
line 42-
$class::addEntry(JText::('COM_TZ_PORTFOLIO_SUBMENU_GROUP_FIELDS'), 'index.php?option=com_tz_portfolio&view=fieldsgroup', $vName == 'fieldsgroup');
how i can i fix it?
Site: eneskuray.com php 5.2.17 litespeed
full php
<?php
// No direct access
defined('_JEXEC') or die;
class TZ_PortfolioHelper
{
public static $extension = 'com_content';
/**
* Configure the Linkbar.
*
* #param string $vName The name of the active view.
*
* #return void
* #since 1.6
*/
public static function addSubmenu($vName)
{
$class = 'JHtmlSidebar';
if(!COM_TZ_PORTFOLIO_JVERSION_COMPARE){
$class = 'JSubMenuHelper';
}
$class::addEntry( JText::_('COM_TZ_PORTFOLIO_SUBMENU_GROUP_FIELDS'), 'index.php?option=com_tz_portfolio&view=fieldsgroup', $vName == 'fieldsgroup');
$class::addEntry(
JText::_('COM_TZ_PORTFOLIO_SUBMENU_FIELDS'),
'index.php?option=com_tz_portfolio&view=fields',
$vName == 'fields'
);
$class::addEntry(
JText::_('COM_TZ_PORTFOLIO_SUBMENU_CATEGORIES'),
'index.php?option=com_tz_portfolio&view=categories',
$vName == 'categories');
$class::addEntry(
JText::_('COM_TZ_PORTFOLIO_SUBMENU_ARTICLES'),
'index.php?option=com_tz_portfolio&view=articles',
$vName == 'articles'
);
$class::addEntry(
JText::_('COM_TZ_PORTFOLIO_SUBMENU_FEATURED_ARTICLES'),
'index.php?option=com_tz_portfolio&view=featured',
$vName == 'featured'
);
$class::addEntry(
JText::_('COM_TZ_PORTFOLIO_SUBMENU_TAGS'),
'index.php?option=com_tz_portfolio&view=tags',
$vName == 'tags');
$class::addEntry(
JText::_('COM_TZ_PORTFOLIO_SUBMENU_USERS'),
'index.php?option=com_tz_portfolio&view=users',
$vName == 'users');
}
/**
* Gets a list of the actions that can be performed.
*
* #param int The category ID.
* #param int The article ID.
*
* #return JObject
* #since 1.6
*/
public static function getActions($categoryId = 0, $articleId = 0)
{
$user = JFactory::getUser();
$result = new JObject;
if (empty($articleId) && empty($categoryId)) {
$assetName = 'com_tz_portfolio';
}
elseif (empty($articleId)) {
$assetName = 'com_tz_portfolio.category.'.(int) $categoryId;
}
else {
$assetName = 'com_tz_portfolio.article.'.(int) $articleId;
}
$actions = array(
'core.admin', 'core.manage', 'core.create', 'core.edit', 'core.edit.own', 'core.edit.state', 'core.delete'
);
foreach ($actions as $action) {
$result->set($action, $user->authorise($action, $assetName));
}
return $result;
}
/**
* Applies the content tag filters to arbitrary text as per settings for current user group
* #param text The string to filter
* #return string The filtered string
*/
public static function filterText($text)
{
// Filter settings
$config = JComponentHelper::getParams('com_config');
$user = JFactory::getUser();
$userGroups = JAccess::getGroupsByUser($user->get('id'));
$filters = $config->get('filters');
$blackListTags = array();
$blackListAttributes = array();
$customListTags = array();
$customListAttributes = array();
$whiteListTags = array();
$whiteListAttributes = array();
$noHtml = false;
$whiteList = false;
$blackList = false;
$customList = false;
$unfiltered = false;
// Cycle through each of the user groups the user is in.
// Remember they are included in the Public group as well.
foreach ($userGroups as $groupId)
{
// May have added a group but not saved the filters.
if (!isset($filters->$groupId)) {
continue;
}
// Each group the user is in could have different filtering properties.
$filterData = $filters->$groupId;
$filterType = strtoupper($filterData->filter_type);
if ($filterType == 'NH') {
// Maximum HTML filtering.
$noHtml = true;
}
elseif ($filterType == 'NONE') {
// No HTML filtering.
$unfiltered = true;
}
else {
// Black, white or custom list.
// Preprocess the tags and attributes.
$tags = explode(',', $filterData->filter_tags);
$attributes = explode(',', $filterData->filter_attributes);
$tempTags = array();
$tempAttributes = array();
foreach ($tags as $tag)
{
$tag = trim($tag);
if ($tag) {
$tempTags[] = $tag;
}
}
foreach ($attributes as $attribute)
{
$attribute = trim($attribute);
if ($attribute) {
$tempAttributes[] = $attribute;
}
}
// Collect the black or white list tags and attributes.
// Each lists is cummulative.
if ($filterType == 'BL') {
$blackList = true;
$blackListTags = array_merge($blackListTags, $tempTags);
$blackListAttributes = array_merge($blackListAttributes, $tempAttributes);
}
elseif ($filterType == 'CBL') {
// Only set to true if Tags or Attributes were added
if ($tempTags || $tempAttributes) {
$customList = true;
$customListTags = array_merge($customListTags, $tempTags);
$customListAttributes = array_merge($customListAttributes, $tempAttributes);
}
}
elseif ($filterType == 'WL') {
$whiteList = true;
$whiteListTags = array_merge($whiteListTags, $tempTags);
$whiteListAttributes = array_merge($whiteListAttributes, $tempAttributes);
}
}
}
// Remove duplicates before processing (because the black list uses both sets of arrays).
$blackListTags = array_unique($blackListTags);
$blackListAttributes = array_unique($blackListAttributes);
$customListTags = array_unique($customListTags);
$customListAttributes = array_unique($customListAttributes);
$whiteListTags = array_unique($whiteListTags);
$whiteListAttributes = array_unique($whiteListAttributes);
// Unfiltered assumes first priority.
if ($unfiltered) {
// Dont apply filtering.
}
else {
// Custom blacklist precedes Default blacklist
if ($customList) {
$filter = JFilterInput::getInstance(array(), array(), 1, 1);
// Override filter's default blacklist tags and attributes
if ($customListTags) {
$filter->tagBlacklist = $customListTags;
}
if ($customListAttributes) {
$filter->attrBlacklist = $customListAttributes;
}
}
// Black lists take third precedence.
elseif ($blackList) {
// Remove the white-listed attributes from the black-list.
$filter = JFilterInput::getInstance(
array_diff($blackListTags, $whiteListTags), // blacklisted tags
array_diff($blackListAttributes, $whiteListAttributes), // blacklisted attributes
1, // blacklist tags
1 // blacklist attributes
);
// Remove white listed tags from filter's default blacklist
if ($whiteListTags) {
$filter->tagBlacklist = array_diff($filter->tagBlacklist, $whiteListTags);
}
// Remove white listed attributes from filter's default blacklist
if ($whiteListAttributes) {
$filter->attrBlacklist = array_diff($filter->attrBlacklist);
}
}
// White lists take fourth precedence.
elseif ($whiteList) {
$filter = JFilterInput::getInstance($whiteListTags, $whiteListAttributes, 0, 0, 0); // turn off xss auto clean
}
// No HTML takes last place.
else {
$filter = JFilterInput::getInstance();
}
$text = $filter->clean($text, 'html');
}
return $text;
}
}
ATTETION : some texts deleted for privacy.

The scope resolution operator (::) can only be used on a class referenced using a variable from PHP 5.3 -- you're using 5.2.
You'd have to do JHtmlSidebar::addEntry or JSubMenuHelper::addEntry; you can't do $class::addEntry.

JText::('COM_TZ_PORTFOLIO_SUBMENU_GROUP_FIELDS') is not calling a method. It should be:
JText::_('COM_TZ_PORTFOLIO_SUBMENU_GROUP_FIELDS')
See JText.

T_PAAMAYIM_NEKUDOTAYIM refers to the two colons in a row like this ::. Looking at your code sample:
$class::addEntry( JText::('COM_TZ_PORTFOLIO_SUBMENU_GROUP_FIELDS'), 'index.php?option=com_tz_portfolio&view=fieldsgroup', $vName == 'fieldsgroup');
I believe the issue is with JText::(. As per Joomla documentation that should be formatted with an underscore so it is JText::_( so your code would be:
$class::addEntry( JText::_('COM_TZ_PORTFOLIO_SUBMENU_GROUP_FIELDS'), 'index.php?option=com_tz_portfolio&view=fieldsgroup', $vName == 'fieldsgroup');
Not 100% clear on Joomla internals, but that underscore (_) is actually a function/method of some sort within the JText class. So when you were calling it as JText::( PHP choked because it had no idea what you were trying to do with JText. By adding that underscore (_), it will now actually call a function within a class & do what it has to do.

Related

Whenever comment on any issue, last comment username not update in issues

Whenever comment on any one of the issues, comment save successfully.
Comment saving code in given below:
$user = elgg_get_logged_in_user_entity();
$p_url = parse_url($_SERVER['HTTP_REFERER']);
if($p_url['path'] == '/issues/view' || $p_url['path'] == '/issues/respond')
$tracker_comment = TRUE;
else
$tracker_comment = FALSE;
if($tracker_comment)
{
action_gatekeeper();
// Get input
$entity_guid = (int) get_input('entity_guid');
$comment_text = get_input('generic_comment');
// Let's see if we can get an entity with the specified GUID
if ($entity = get_entity($entity_guid)) {
$comment = new ElggComment();
$comment->description = $comment_text;
$comment->owner_guid = $user->getGUID();
$comment->container_guid = $entity->getGUID();
$comment->access_id = $entity->access_id;
$guid = $comment->save();
// If posting the comment was successful, say so
if ($guid) {
system_message(elgg_echo("generic_comment:posted"));
} else {
register_error(elgg_echo("generic_comment:failure"));
}
} else {
register_error(elgg_echo("generic_comment:notfound"));
}
// Forward to the
forward($entity->getURL());
}
else
RETURN TRUE;
I am unable to retrieve the last comment username not updated in list of issues. I am using elgg_get_annotation() to retrieve the last comment details.
But not retrieving the details. Last comment code in given bellow.
if ($table_rows) {
foreach ( $table_rows as $entity ) {
if ($entity->unread == 1) {
$unread = "Yes";
} else {
$unread = "No";
}
if ($entity->assigned_to == 0) {
$assigned = "No";
} else {
$assigned = "Yes";
}
$last_options = array ();
$comments = elgg_get_annotations(array(
'annotation_names' => 'issue_tracker_changes',
'guid' => $entity->guid,
'limit' => 1,
'order_by' => 'n_table.id DESC',
));
foreach ( $comments as $comment ) {
$last_comment = get_entity ( $comment->owner_guid );
}
Comments are no longer annotations since Elgg 1.9 but entities. You are creating comment using ElggComment class that represents entity so you're using Elgg 1.9 or newer. You just need to use elgg_get_entities instead of elgg_get_annotations. Use type object and subtype comment.

Moodle 2.7 Need to know how to create custom menu for different user groups

I have been working on moodle 2.7 framework, my requirement is i wanted to show 2 different custom menu for Teacher and Student Role, at present I have created custom menu in template options, but I have no idea how to create multiple custom menu in moodle. so far I tried doing it by searching group id and then use if statement to assign custom menu but still no output, so I request can anybody help me to sort this issues.
You can extend the custom menu - https://docs.moodle.org/dev/Extending_the_theme_custom_menu
Then use has_capability() to see if the user can see the menu item.
Something like this (I haven't tried it)
// theme/themename/renderers.php
class theme_themename_core_renderer extends core_renderer {
protected function render_custom_menu(custom_menu $menu) {
$context = context_system::instance();
if (has_capability('local_plugin/menuname:view', $context)) {
$branchlabel = get_string('menuname', 'local_myplugin');
$branchurl = new moodle_url('/local/myplugin/index.php');
$branchtitle = $branchlabel;
$branchsort = 10000;
$branch = $menu->add($branchlabel, $branchurl, $branchtitle, $branchsort);
}
return parent::render_custom_menu($menu);
}
}
Russell,
below is code from renderes.php
<?php
/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
This plugin is part of Archaius theme.
#copyright 2013 Daniel Munera Sanchez
*/
class theme_archaius_core_renderer extends core_renderer {
/**
* Prints a nice side block with an optional header.
*
* The content is described
* by a {#link block_contents} object.
*
* #param block_contents $bc HTML for the content
* #param string $region the region the block is appearing in.
* #return string the HTML to be output.
*/
function block(block_contents $bc, $region) {
$bc = clone($bc); // Avoid messing up the object passed in.
if (empty($bc->blockinstanceid) || !strip_tags($bc->title)) {
$bc->collapsible = block_contents::NOT_HIDEABLE;
}
if ($bc->collapsible == block_contents::HIDDEN) {
$bc->add_class('hidden');
}
if (!empty($bc->controls)) {
$bc->add_class('block_with_controls');
}
$skiptitle = strip_tags($bc->title);
if (empty($skiptitle)) {
$output = '';
$skipdest = '';
} else {
$output = html_writer::tag('a', get_string('skipa', 'access', $skiptitle), array('href' => '#sb-' . $bc->skipid, 'class' => 'skip-block'));
$skipdest = html_writer::tag('span', '', array('id' => 'sb-' . $bc->skipid, 'class' => 'skip-block-to'));
}
$title = '';
if ($bc->title) {
$title = html_writer::tag('h2', $bc->title);
}
$controlshtml = $this->block_controls($bc->controls);
if ($title || $controlshtml) {
$output .= html_writer::tag('div', html_writer::tag('div', $title , array('class' => 'title')), array('class' => 'header-tab'));
}
$output .= html_writer::start_tag('div', $bc->attributes);
if ($title || $controlshtml) {
$output .= html_writer::tag('div', html_writer::tag('div', html_writer::tag('div', '', array('class'=>'block_action')). $title . $controlshtml, array('class' => 'title')), array('class' => 'header'));
}
$output .= html_writer::start_tag('div', array('class' => 'content'));
if (!$title && !$controlshtml) {
$output .= html_writer::tag('div', '', array('class'=>'block_action notitle'));
}
$output .= $bc->content;
if ($bc->footer) {
$output .= html_writer::tag('div', $bc->footer, array('class' => 'footer'));
}
$output .= html_writer::end_tag('div');
$output .= html_writer::end_tag('div');
if ($bc->annotation) {
$output .= html_writer::tag('div', $bc->annotation, array('class' => 'blockannotation'));
}
$output .= $skipdest;
$this->init_block_hider_js($bc);
return $output;
}
/*
* From boostrapbase
* Overriding the custom_menu function ensures the custom menu is
* always shown, even if no menu items are configured in the global
* theme settings page.
*/
public function custom_menu($custommenuitems = '') {
global $CFG;
//TODO: Check this in a different way
$langs = get_string_manager()->get_list_of_translations();
if ( (count($langs) < 2) and (empty($CFG->custommenuitems))) {
return '';
}else{
if (!empty($CFG->custommenuitems))
$custommenuitems .= $CFG->custommenuitems;
$custommenu = new custom_menu($custommenuitems, current_language());
return $this->render_custom_menu($custommenu);
}
}
// http://docs.moodle.org/dev/Extending_the_theme_custom_menu
protected function render_custom_menu(custom_menu $menu) {
global $CFG;
require_once($CFG->dirroot.'/course/lib.php');
//navigation mycourses is no supported since 2.4
if (isloggedin() && !isguestuser() &&
$mycourses = enrol_get_my_courses(NULL, 'visible DESC, fullname ASC')) {
$branchlabel = get_string('mycourses') ;
$branchurl = new moodle_url('/course/index.php');
$branchtitle = $branchlabel;
$branchsort = 8000 ;
$branch = $menu->add($branchlabel, $branchurl, $branchtitle, $branchsort);
foreach ($mycourses as $mycourse) {
$branch->add($mycourse->shortname, new moodle_url(
'/course/view.php',
array('id' => $mycourse->id)),
$mycourse->fullname
);
}
}
$course_id = $this->page->course->id;
if (isloggedin() && $course_id > 1) {
$branchlabel = get_string('grades');
$branchurl = new moodle_url('/grade/report/index.php?id='.$this->page->course->id);
$branchtitle = $branchlabel;
$branchsort = 9000;
$branch = $menu->add($branchlabel, $branchurl, $branchtitle, $branchsort);
}
//From boostrapbase
// TODO: eliminate this duplicated logic, it belongs in core, not
// here. See MDL-39565.
$addlangmenu = true;
$langs = get_string_manager()->get_list_of_translations();
if (
count($langs) < 2
or empty($CFG->langmenu)
or ($this->page->course != SITEID
and !empty($this->page->course->lang))) {
$addlangmenu = false;
}
if ($addlangmenu) {
$branchlabel = get_string('language');
$branchurl = new moodle_url('#');
$branch = $menu->add($branchlabel, $branchurl, $branchlabel, 10000);
foreach ($langs as $langtype => $langname) {
$branch->add($langname,
new moodle_url(
$this->page->url,
array('lang' => $langtype)
),
$langname
);
}
}
return parent::render_custom_menu($menu);
}
protected function render_custom_menu_item(custom_menu_item $menunode) {
$transmutedmenunode = new theme_archaius_transmuted_custom_menu_item($menunode);
return parent::render_custom_menu_item($transmutedmenunode);
}
}

Bcmath adding decimal places randomly

I have a PHP function I got from the web that uses bcmath functions:
function SteamID64to32($steamId64) {
$iServer = "1";
if(bcmod($steamId64, "2") == "0") {
$iServer = "0";
}
$steamId64 = bcsub($steamId64,$iServer);
if(bccomp("76561197960265728",$steamId64) == -1) {
$steamId64 = bcsub($steamId64,"76561197960265728");
}
$steamId64 = bcdiv($steamId64, "2");
return ("STEAM_0:" . $iServer . ":" . $steamId64);
}
For any valid input the function will at random times add ".0000000000" to the output.
Ex:
$input = 76561198014791430;
SteamID64to32($input);
//result for each run
STEAM_0:0:27262851
STEAM_0:0:27262851.0000000000
STEAM_0:0:27262851
STEAM_0:0:27262851
STEAM_0:0:27262851.0000000000
STEAM_0:0:27262851
STEAM_0:0:27262851.0000000000
STEAM_0:0:27262851.0000000000
STEAM_0:0:27262851.0000000000
This was tested on 2 different nginx servers running php-fpm.
Please help me understand what is wrong here. Thanks
The answer provided by JD doesn't account for all possible STEAM_ ID types, namely, anything that is in the STEAM_0:1 range. This block of code will:
<?php
function Steam64ToSteam32($Steam64ID)
{
$offset = $Steam64ID - 76561197960265728;
$id = ($offset / 2);
if($offset % 2 != 0)
{
$Steam32ID = 'STEAM_0:1:' . bcsub($id, '0.5');
}
else
{
$Steam32ID = "STEAM_0:0:" . $id;
}
return $Steam32ID;
}
echo Steam64ToSteam32(76561197960435530);
echo "<br/>";
echo Steam64ToSteam32(76561197990323733);
echo "<br/>";
echo Steam64ToSteam32(76561198014791430);
?>
This outputs
STEAM_0:0:84901
STEAM_0:1:15029002
STEAM_0:0:27262851
The first one is for a Valve employee and someone who's had a Steam account since 2003 (hence the low ID), the second is a random profile I found on VACBanned which had an ID in the STEAM_0:1 range. The third is the ID you provided in your sample code.
I found this: SteamProfile
/**
* This file is part of SteamProfile.
*
* Written by Nico Bergemann <barracuda415#yahoo.de>
* Copyright 2009 Nico Bergemann
*
* SteamProfile is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SteamProfile 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with SteamProfile. If not, see <http://www.gnu.org/licenses/>.
*/
// thanks to voogru for the id transformation algorithm (http://forums.alliedmods.net/showthread.php?t=60899)
class SteamID {
private $sSteamID = '';
private $sSteamComID = '';
const STEAMID64_BASE = '76561197960265728';
public function __construct($sID) {
// make sure the bcmath extension is loaded
if(!extension_loaded('bcmath')) {
throw new RuntimeException("BCMath extension required");
}
if($this->isValidSteamID($sID)) {
$this->sSteamID = $sID;
$this->sSteamComID = $this->convertToSteamComID($sID);
} elseif($this->isValidComID($sID)) {
$this->sSteamID = $this->convertToSteamID($sID);
$this->sSteamComID = $sID;
} else {
$this->sSteamID = '';
$this->sSteamComID = '';
}
}
public function getSteamID() {
return $this->sSteamID;
}
public function getSteamComID() {
return $this->sSteamComID;
}
public function isValid() {
return $this->sSteamID != '';
}
private function isValidSteamID($sSteamID) {
return preg_match('/^(STEAM_)?[0-5]:[0-9]:\d+$/i', $sSteamID);
}
private function isValidComID($sSteamComID) {
// anything else than a number is invalid
// (is_numeric() doesn't work for 64 bit integers)
if(!preg_match('/^\d+$/i', $sSteamComID)) {
return false;
}
// the community id must be bigger than STEAMID64_BASE
if(bccomp(self::STEAMID64_BASE, $sSteamComID) == 1) {
return false;
}
// TODO: Upper limit?
return true;
}
private function convertToSteamComID($sSteamID) {
$aTMP = explode(':', $sSteamID);
$sServer = $aTMP[1];
$sAuth = $aTMP[2];
if((count($aTMP) == 3) && $sAuth != '0' && is_numeric($sServer) && is_numeric($sAuth)) {
$sComID = bcmul($sAuth, "2"); // multipy Auth-ID with 2
$sComID = bcadd($sComID, $sServer); // add Server-ID
$sComID = bcadd($sComID, self::STEAMID64_BASE); // add this odd long number
// It seems that PHP appends ".0000000000" at the end sometimes.
// I can't find a reason for this, so I'll take the dirty way...
$sComID = str_replace('.0000000000', '', $sComID);
return $sComID;
} else {
throw new RuntimeException("Unable to convert Steam-ID");
}
}
private function convertToSteamID($sSteamComID) {
$sServer = bcmod($sSteamComID, '2') == '0' ? '0' : '1';
$sCommID = bcsub($sSteamComID, $sServer);
$sCommID = bcsub($sCommID, self::STEAMID64_BASE);
$sAuth = bcdiv($sCommID, '2');
return "STEAM_0:$sServer:$sAuth";
}
}

Codeigniter DataGrid Class

So, I am building myself a simple(ish) datagrid class for my CodeIgniter app.
What I am wondering is, I have some columns that I would like to "format" in that I mean, some may contain only a 1 or a 0, yet I want to turn them into a Yes or No respectively.
How can I do this? In other words, I want to be able to pass in another parameter..something like:
$this->_columnCallBack pass it an array like array(column_number=>'NameOfCallBackFunction')
I am assuming that I would do it somewhat like I did the _columnclass, where I pass in the column number, and the class as an array... but I don't know how I would get the function to fire off to do the replacement...
Code
class O7thDG {
public function __construct($params){
$this->_table = $params['table'];
$this->_pk = $params['pk'];
$this->_fields = (isset($params['fields'])) ? $params['fields'] : null;
$this->_where = (isset($params['where'])) ? $params['where'] : null;
$this->_order = (isset($params['order'])) ? $params['order'] : null;
$this->_extras = (isset($params['extras'])) ? $params['extras'] : null;
$this->_add = (isset($params['add'])) ? $params['add'] : FALSE;
$this->_edit = (isset($params['edit'])) ? $params['edit'] : FALSE;
$this->_delete = (isset($params['delete'])) ? $params['delete'] : FALSE;
$this->_editlink = (isset($params['editlink'])) ? $params['editlink'] : null;
$this->_deletelink = (isset($params['deletelink'])) ? $params['deletelink'] : null;
$this->_editlinkextras = (isset($params['editlinkextras'])) ? $params['editlinkextras'] : null;
$this->_deletelinkextras = (isset($params['deletelinkextras'])) ? $params['deletelinkextras'] : null;
$this->_tableid = (isset($params['tableid'])) ? $params['tableid'] : null;
$this->_tableclass = (isset($params['tableclass'])) ? $params['tableclass'] : null;
$this->_columnclass = (isset($params['columnclass'])) ? $params['columnclass'] : null;
$this->_includeheader = (isset($params['includeheader'])) ? $params['includeheader'] : TRUE;
$this->_allowpaging = (isset($params['allowpaging'])) ? $params['allowpaging'] : FALSE;
$this->_sorting = (isset($params['sorting'])) ? $params['sorting'] : null;
$this->_columncallback = (isset($params['columncallback'])) ? $params['columncallback'] : null;
}
public function BuildIt($responsive = TRUE){
$_ci =& get_instance();
$_ci->load->database();
$_ci->load->library('table');
$_ci->load->library('TKCommon', null, 'comm');
$fldlist = $this->_buildSelectFieldList();
$_ci->db->select($fldlist);
$cols = $this->_buildColumnFieldList();
$ret = '';
if($this->_where != null){
// build the where
}
if($this->_order != null){
// build the order
}
if($this->_extras != null){
// build the extras
}
// Query the specified table
$qry = $_ci->db->get($this->_table);
if($cols == null){
$cols = $_ci->db->list_fields($this->_table);
$fldlist = $cols;
}else{
$fldlist = explode(', ', $fldlist);
}
if($qry){
// throw the results into an associative array
$rs = $qry->result_array();
if($rs){
$rCt = count($rs);
$cCt = $qry->num_fields();
// add our responsive wrapper
if($responsive){
$ret .= '<div class="table-responsive">';
}
// fire up our table
$tid = '';
$tc = '';
if($this->_tableid != null){$tid = ' id="' .$this->_tableid . '"';}
if($this->_tableclass != null){$tc = ' class="' .$this->_tableclass . '"';}
$_ci->table->set_template(array('table_open'=>'<table' . $tid . $tc . '>'));
// build our header row, but only if we need to
if($this->_includeheader && $cCt > 0){
// see if we need to include the admin column
if($this->_edit || $this->_delete){
$_ci->table->set_heading(array_merge($cols, array('Admin')));
}else{
$_ci->table->set_heading($cols);
}
}
// build each records row
for($r = 0; $r < $rCt; ++$r){
$ca = array();
for($c = 0; $c < $cCt; ++$c){
if(($this->_columnclass != null) && ($c == key($this->_columnclass))){
// figure out which column needs the class, and what class needs to be applied
$ca[] = $this->_columnCallback($c, array('data'=>$rs[$r][$fldlist[$c]], 'class'=>$this->_columnclass[key($this->_columnclass)]));
}else{
$ca[] = $this->_columnCallback($c, $rs[$r][$fldlist[$c]]);
}
}
// see if we need to include the admin column
if(($this->_edit || $this->_delete) && ($this->_editlink != null || $this->_deletelink != null)){
$txt = '';
if($this->_edit &&($this->_editlink != null)){
$txt .= '<span class="fa fa-pencil fa-lg"></span> ';
}
if($this->_delete &&($this->_deletelink != null)){
$txt .= '<span class="fa fa-trash-o fa-lg"></span>';
}
if(($this->_columnclass != null) && ($cCt == key($this->_columnclass))){
$ca[] = array('data'=>$txt, 'class'=>$this->_columnclass[key($this->_columnclass)]);
}else{
$ca[] = $txt;
}
}
$_ci->table->add_row($ca);
}
$ret .= $_ci->table->generate();
// close our responsive wrapper
if($responsive){
$ret .= '</div>';
}
}else{
$ret .= $_ci->comm->ErrorBox('There was an issue running the query, please make sure at least your primary key, and table are correct.');
}
}else{
$ret .= $_ci->comm->ErrorBox('There was an issue running the query, please make sure at least your primary key, and table are correct.');
}
return $ret;
}
// build our select's field list
private function _buildSelectFieldList(){
if($this->_fields == null){
return '*';
}else{
$flds = array_map(function($item){return $item['field'];}, $this->_fields);
return implode(', ', $flds);
}
}
// build our tables column list
private function _buildColumnFieldList(){
if($this->_fields == null){
return null;
}else{
return array_map(function($item){return $item['label'];}, $this->_fields);
}
}
private function _columnCallback($col, $val){
if($this->_columncallback != null){
if($col == key($this->_columncallback))
return $this->_columncallback[key($this->_columncallback)]($val);
}else{
return $val;
}
}
}
and my external function that I may want to use is simply:
// Format boolean value to Yes or No
public function YesNo($val){
return ((bool)$val) ? 'Yes' : 'No' ;
}
CI Documentation for the table class has $this->table->function, however, the function(s) passed applies to the entire table
What about using and/or anonymous functions:
http://docs.php.net/manual/es/function.func-get-args.php
http://php.net/manual/en/functions.anonymous.php
Update 2 "reloaded"
You could also check grocery crud, it's CI library and do some interesting things that can be useful for you project:
http://www.grocerycrud.com/
Grocery crud uses call_user_func from PHP and allows you to use any function declaread in your controller, if this is what you need then it's just matter of time and check grocery crud code.
In the library a protected property is declared for each callback for example (line #3386 v1.4.1):
/* Callbacks */
protected $callback_before_insert = null;
So you/me/any can set the callback function in case is needed, then check for any callback setted doing something like (line #878 GroceryCrud 1.4.1):
if($this->callback_before_insert !== null)
{
$callback_return = call_user_func($this->callback_before_insert, $post_data);
if(!empty($callback_return) && is_array($callback_return))
$post_data = $callback_return;
elseif($callback_return === false)
return false;
}
Of course there's a method to set the callback (line #4518 from v1.4.1):
public function callback_before_insert($callback = null)
{
$this->callback_before_insert = $callback;
return $this;
}
And the user/dev set the callback doing:
$crud->callback_before_insert(array($this,'my_callback'));
And/Or this technique allows you use something like:
$this->load->model('Customers');
$crud->callback_before_insert(array($this->Customers,'getCustomersCallback'));
even if you're usign php 5.3 or greater you can use an anonymous method:
$crud->callback_before_insert(function($post_array){
$post_array['user_id'] = $this->session->userdata('user_id');
return $post_array;
});
More info: http://www.grocerycrud.com/documentation/tutorial_using_callbacks
About Grocery Crud:
Author: John Skoumbourdis (more about author/library)
Web: GroceryCrud
License: released with dual licensing, using the GPL v3 and the MIT license.
Update 3
Update I've read more carefully your question, thinking functions varible can work for you:
http://php.net/manual/es/functions.variable-functions.php
See the first and second samples here: http://www.php.net/manual/en/language.oop5.php
Still don't know if they will work "outside" your class, but it could.
These functions will work as you want to, but you need to rewrite a bit for your needs.
function callback1($array, $row_id, $function)
{
if(function_exists($function))
return $function($array[$row_id]);
else
return 0;
}
function YesNo($val)
{
return intval($val) ? 'Yes' : 'No' ;
}
#example of array and usage of script.
$array = array('id' => '0', 'id2' => '1');
$ok = callback1($array, 'id2', 'YesNo');
Make filters array
$this->filters = ['column_name' => 'function_name', 'column_name2' => 'otherFunction'];
Then you just apply the filter to the data
if ( array_key_exists($column_name, $this->filters) ) {
// Add row that contains filtered data
$row_data = $this->filters[$column_name]($column_val);
}else{
$row_data = $column_val;
}
If your functions aren't available globally (not in a helper or built-in php), you'll have to use something like this:
call_user_func(array($this, $this->filters[$column_name]), $column_val);
OR
$this->{$this->filters[$column_name]}($column_val);
This would run a function from inside the library O7...

Magento BestSeller Module - Summing Configurable Products And Adding Them Back In

This has been bugging me for quite a while. Basically, what we are trying to achieve is in the bestsellers on our front page, to have the products listed in the amount sold. For simple products this works fine, however for configurable products they will be displayed as a quantity ordered of 0.
I somehow need to find a way to get the configurable products, find the simple products attached to them, sum the amount sold of these simple products, add this back to the configurable products ID and feed this information back in so it will list the configurable product with the right amount that has been sold.
I have placed, what I believe, the areas of code that require changing. If anyone could help it would be very much appreciated!
Collection.php
class Luxe_Bestsellers_Model_Mysql4_Product_Collection extends Mage_Reports_Model_Mysql4_Product_Collection
{
public function addOrderedQty($from = '', $to = '', $getComplexProducts=false)
{
$qtyOrderedTableName = $this->getTable('sales/order_item');
$qtyOrderedFieldName = 'qty_ordered';
$productIdFieldName = 'product_id';
if (!$getComplexProducts) {
$compositeTypeIds = Mage::getSingleton('catalog/product_type')->getCompositeTypes();
$productTypes = $this->getConnection()->quoteInto(' AND (e.type_id NOT IN (?))', $compositeTypeIds);
} else {
$productTypes = '';
}
if ($from != '' && $to != '') {
$dateFilter = " AND `order`.created_at BETWEEN '{$from}' AND '{$to}'";
} else {
$dateFilter = "";
}
$this->getSelect()->reset()->from(
array('order_items' => $qtyOrderedTableName),
array('ordered_qty' => "SUM(order_items.{$qtyOrderedFieldName})")
);
$_joinCondition = $this->getConnection()->quoteInto(
'order.entity_id = order_items.order_id AND order.state<>?', Mage_Sales_Model_Order::STATE_CANCELED
);
$_joinCondition .= $dateFilter;
$this->getSelect()->joinInner(
array('order' => $this->getTable('sales/order')),
$_joinCondition,
array()
);
$this->getSelect()
->joinInner(array('e' => $this->getProductEntityTableName()),
"e.entity_id = order_items.{$productIdFieldName} AND e.entity_type_id = {$this->getProductEntityTypeId()}{$productTypes}")
->group('e.entity_id')
->having('ordered_qty > 0');
return $this;
}
}
List.php
class Luxe_Bestsellers_Block_List extends Mage_Catalog_Block_Product_List
{
protected $_defaultToolbarBlock = 'bestsellers/list_toolbar';
protected function _beforeToHtml() {
$this->addPriceBlockType('bundle', 'bundle/catalog_product_price', 'bundle/catalog/product/price.phtml');
return parent::_beforeToHtml();
}
public function _toHtml()
{
if ($this->_productCollection->count()) {
return parent::_toHtml();
} else {
return '';
}
}
public function getTimeLimit()
{
if ($this->getData('time_limit_in_days')) {
return intval($this->getData('time_limit_in_days'));
} else {
return intval(Mage::getStoreConfig('bestsellers/bestsellers/time_limit_in_days'));
}
}
public function getBlockTitle()
{
if ($this->getData('title')) {
return $this->getData('title');
} else {
return Mage::getStoreConfig('bestsellers/bestsellers/title');
}
}
public function isShowOutOfStock() {
return (bool)Mage::getStoreConfig('bestsellers/bestsellers/show_out_of_stock');
}
public function getProductsLimit()
{
if ($this->getData('limit')) {
return intval($this->getData('limit'));
} else {
return $this->getToolbarBlock()->getLimit();
}
}
public function getDisplayMode()
{
return $this->getData('display_mode');
}
/**
* Retrieve loaded category collection
*
* #return Mage_Eav_Model_Entity_Collection_Abstract
*/
protected function _getProductCollection()
{
if (is_null($this->_productCollection)) {
$layer = Mage::getModel('catalog/layer');
$bestsellers = Mage::getResourceModel('reports/product_collection');
if ($this->getTimeLimit()) {
$product = Mage::getModel('catalog/product');
$todayDate = $product->getResource()->formatDate(time());
$startDate = $product->getResource()->formatDate(time() - 60 * 60 * 24 * $this->getTimeLimit());
$bestsellers->addOrderedQty($startDate, $todayDate, true);
} else {
$bestsellers->addOrderedQty('', '', true);
}
$bestsellers->addStoreFilter()
->setOrder('ordered_qty', 'desc')
->setPageSize($this->getProductsLimit());
Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($bestsellers);
if ($layer->getCurrentCategory()->getId() != Mage::app()->getStore()->getRootCategoryId()) {
$bestsellers->addCategoryFilter($layer->getCurrentCategory());
Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($bestsellers);
}
if (!$this->isShowOutOfStock()) {
Mage::getModel('cataloginventory/stock')->addInStockFilterToCollection($bestsellers);
}
$bestsellers->getSelect()->where('order.store_id = ?', Mage::app()->getStore()->getId());
$productIds = array();
foreach ($bestsellers as $p) {
$productIds[] = $p->getId();
}
$collection = Mage::getResourceModel('catalog/product_collection');
Mage::getModel('catalog/layer')->prepareProductCollection($collection);
$attributes = Mage::getSingleton('catalog/config')->getProductAttributes();
$collection->addIdFilter($productIds)
->addAttributeToSelect($attributes)
->addMinimalPrice()
->addFinalPrice();
$this->_productCollection = $collection;
}
return $this->_productCollection;
}
/**
* Translate block sentence
*
* #return string
*/
public function __()
{
$args = func_get_args();
$expr = new Mage_Core_Model_Translate_Expr(array_shift($args), 'Mage_Catalog');
array_unshift($args, $expr);
return Mage::app()->getTranslator()->translate($args);
}
}
Thanks for posting that sample code! I was able to use it to create a solution which should work well for both of us.
I found that configurable product sales are being summed correctly but aren't being included in the results; their child products appear instead. My solution was to include configurable products, do a left join on the catalog_product_super_link table, and filter out anything that has a parent_id. Here are the changes you'll need to make:
Collection.php:
public function addOrderedQty($from = '', $to = '', $getComplexProducts=false, $getComplexChildProducts = true, $getRemovedProducts = true)
{
$qtyOrderedTableName = $this->getTable('sales/order_item');
$qtyOrderedFieldName = 'qty_ordered';
$productIdFieldName = 'product_id';
if (!$getComplexProducts) {
$compositeTypeIds = Mage::getSingleton('catalog/product_type')->getCompositeTypes();
$productTypes = $this->getConnection()->quoteInto(' AND (e.type_id NOT IN (?))', $compositeTypeIds);
} else {
$productTypes = '';
}
if ($from != '' && $to != '') {
$dateFilter = " AND `order`.created_at BETWEEN '{$from}' AND '{$to}'";
} else {
$dateFilter = "";
}
$this->getSelect()->reset()->from(
array('order_items' => $qtyOrderedTableName),
array(
'ordered_qty' => "SUM(order_items.{$qtyOrderedFieldName})",
'order_items_name' => 'order_items.name'
)
);
$_joinCondition = $this->getConnection()->quoteInto(
'order.entity_id = order_items.order_id AND order.state<>?', Mage_Sales_Model_Order::STATE_CANCELED
);
$_joinCondition .= $dateFilter;
$this->getSelect()->joinInner(
array('order' => $this->getTable('sales/order')),
$_joinCondition,
array()
);
// Add join to get the parent id for configurables
$this->getSelect()->joinLeft(
array('cpsl' => $this->getTable('catalog/product_super_link')),
'cpsl.product_id = order_items.product_id',
'cpsl.parent_id'
);
if(!$getComplexChildProducts)
$this->getSelect()->having('parent_id IS NULL');
if($getRemovedProducts)
{
$this->getSelect()
->joinLeft(array('e' => $this->getProductEntityTableName()),
"e.entity_id = order_items.{$productIdFieldName} AND e.entity_type_id = {$this->getProductEntityTypeId()}{$productTypes}")
->group('order_items.product_id');
}
else
{
$this->getSelect()
->joinInner(array('e' => $this->getProductEntityTableName()),
"e.entity_id = order_items.{$productIdFieldName} AND e.entity_type_id = {$this->getProductEntityTypeId()}{$productTypes}")
->group('e.entity_id');
}
$this->getSelect()->having('ordered_qty > 0');
// This line is for debug purposes, in case you'd like to see what the SQL looks like
// $x = $this->getSelect()->__toString();
return $this;
}
List.php - Find the following two lines...
$bestsellers->addOrderedQty($startDate, $todayDate, true);
$bestsellers->addOrderedQty('', '', true);
... and change them to:
$bestsellers->addOrderedQty($startDate, $todayDate, true, false, false);
$bestsellers->addOrderedQty('', '', true, false, false);
My changes added two new optional parameters, which both default to true, as to not break existing functionality.
When $getComplexChildProducts is set to false, all child items of the configurable product will be removed from the results.
$getRemovedProducts determines whether or not previously ordered products (which have since been deleted from Magento) should also appear.
Please note that your report statistics will need to be up-to-date in order to get accurate results.
Hope this helps! Let me know if you have any questions.
You can use the following piece of code to get the simple products attached to the configurable product. I'm not sure if this is 100% correct, I haven't tried it myself.
$simpleProducts = Mage::getModel('catalog/product_type_configurable')->getUsedProducts(null, $product);

Categories