Using cakephp model to handle pagination in custom query - pagination

I have 10 subscriber tables from subscriber_0 to subscriber_9, I get all the data from them through union by custom query in subscriber model, in the function getAllSubscribers, this table executes the query and returns the result to the subscriber controller, I have set $useTable = false because there is no subscriber table, my question is how do I paginate this data in the controller?
For reference I am writing my query here and the result returned
Query:
$query = 'SELECT * FROM (
SELECT * FROM subscriber_1
UNION
SELECT * FROM subscriber_2
UNION
SELECT * FROM subscriber_3
UNION
SELECT * FROM subscriber_4
UNION
SELECT * FROM subscriber_5
UNION
SELECT * FROM subscriber_6
UNION
SELECT * FROM subscriber_7
UNION
SELECT * FROM subscriber_8
UNION
SELECT * FROM subscriber_9
UNION
SELECT * FROM subscriber_0
) AS subscriber WHERE created > \'' . $startDate . '\' AND created < \'' . $endDate . '\'';
Result:
array(
(int) 0 => array(
'subscriber' => array(
'a_party' => '923003210861',
'subtype' => '0',
'stat' => '0',
'created' => '2012-11-26 06:53:31',
'updated' => null
)
),
(int) 1 => array(
'subscriber' => array(
'a_party' => '923005264511',
'subtype' => '0',
'stat' => '0',
'created' => '2012-11-26 06:53:31',
'updated' => null
)
)
,
.
.
.
.(int) 50 => ...
I have added this in the subscribers controller
public $paginate = array(
'limit' => 10
);
What else do I need to add?
I have seen this but of no help
CakePHP pass custom query from controller into model paginate()
EDIT: Adding controller code below
$data = $this->Subscriber->getAllSubscribers();//This model method returns custom data from query
$this->paginate = array('fields' => $selectedField);
$paginatedData = $this->paginate($data);
//debug($paginatedData);
$this->set('subslist', $paginatedData);
The above code is not working, what am i doing wrong, thanks

Here is how I achieved it:
Model code
/**
* Overridden paginate method - group by week, away_team_id and home_team_id
*/
public function paginate($conditions,
$fields,
$order,
$limit,
$page = 1,
$recursive = null,
$extra = array()) {
$recursive = -1;
$sql = "SELECT * FROM (
SELECT * FROM blacklist_0
UNION
SELECT * FROM blacklist_1
UNION
SELECT * FROM blacklist_2
UNION
SELECT * FROM blacklist_3
UNION
SELECT * FROM blacklist_4
UNION
SELECT * FROM blacklist_5
UNION
SELECT * FROM blacklist_6
UNION
SELECT * FROM blacklist_7
UNION
SELECT * FROM blacklist_8
UNION
SELECT * FROM blacklist_9
UNION
SELECT * FROM blacklist_0
) AS Blacklist LIMIT " . (($page - 1) * $limit) . ', ' . $limit;
$results = $this->query($sql);
return $results;
}
/**
* Overridden paginateCount method
*/
public function paginateCount($conditions = null,
$recursive = 0,
$extra = array()) {
$sql = "SELECT * FROM (
SELECT * FROM blacklist_1
UNION
SELECT * FROM blacklist_2
UNION
SELECT * FROM blacklist_3
UNION
SELECT * FROM blacklist_4
UNION
SELECT * FROM blacklist_5
UNION
SELECT * FROM blacklist_6
UNION
SELECT * FROM blacklist_7
UNION
SELECT * FROM blacklist_8
UNION
SELECT * FROM blacklist_9
UNION
SELECT * FROM blacklist_0
) AS Blacklist";
if ($conditions['Blacklist.b_party'] <> null) {
$sql = $sql . ' WHERE Blacklist.b_party = \'' . $conditions['Blacklist.b_party'] . '\'';
} else if ($conditions['Blacklist.a_party'] <> null) {
$sql = $sql . ' WHERE Blacklist.a_party = \'' . $conditions['Blacklist.a_party'] . '\'';
}
$this->recursive = $recursive;
$results = $this->query($sql);
return count($results);
}
Not to forget i used
public $useTable = false;
in the model, but the key thing was how to override the methods, which i finally figured out with the help of my collegue, the controller code is simple. Here it is:
Controller code:
public function index() {
$this->set('msisdn', "");
$this->Blacklist->recursive = 0;
$this->set('blacklists', $this->paginate());
}

You can't use standard pagination on custom queries, you'll have to create those yourself.
Custom Query Pagination

Related

Virtuemart search results point to category NOT product details page

Inherited old Joomla site I've upgraded since 2.5 circa 2012, now 3.6.4 with Virtuemart 3.0.16 (using PHP7.0).
Default Joomla search module results appear as:
[Lorem Ipsum product link]
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean accumsan est mi, et volutpat quam blandit a. Etiam blandit, massa ac consequat dapibus product description.
Except the hyperlink of the product title erroneously points to the product category instead of the product detail page.
I believe I have located the issue in this location: /plugins/search/virtuemart/virtuemart.php. Approx. line 223:
$row->virtuemart_product_id . '&virtuemart_category_id=' . $row->cat_id;
I do not know how to change the php into the correct format to point to the product itself. I have tried to change the language from category to product id declaration but this results in mix-matched product links.
How can edit this file to make the product title link point to actual product details page and not the category?
<?php
/**
*
* A search plugin for com_search
*
* #author Valérie Isaksen
* #author Samuel Mehrbrodt
* #version $Id: authorize.php 5122 2011-12-18 22:24:49Z alatak $
* #package VirtueMart
* #subpackage search
* #copyright Copyright (C) 2004-2008 soeren - All rights reserved.
* #license http://www.gnu.org/copyleft/gpl.html GNU/GPL, see LICENSE.php
* VirtueMart is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* See /administrator/components/com_virtuemart/COPYRIGHT.php for copyright notices and details.
*
* http://virtuemart.net
* #modified by Jeno Kovacs --- Offlajn.com 2014
* #description image & price support for Universal AJAX Live Search
*/
// no direct access
defined ('_JEXEC') or die('Restricted access');
class PlgSearchVirtuemart extends JPlugin {
/**
* #return array An array of search areas
*/
function onContentSearchAreas () {
$this->loadLanguage();
static $areas = array(
'virtuemart' => 'PLG_SEARCH_VIRTUEMART_PRODUCTS'
);
return $areas;
}
/**
* Content Search method
* The sql must return the following fields that are used in a common display
* routine: href, title, section, created, text, browsernav
*
* #param string $text Target search string
* #param string $phrase matching option, exact|any|all
* #param string $ordering ordering option, newest|oldest|popular|alpha|category
* #param mixed $areas An array if the search it to be restricted to areas, null if search all
*
* #return array An array of database result objects
*/
function onContentSearch ($text, $phrase = '', $ordering = '', $areas = NULL) {
$db = JFactory::getDbo();
if (is_array($areas)) {
if (!array_intersect ($areas, array_keys ($this->onContentSearchAreas()))) {
return array();
}
}
$limit = $this->params->get('search_limit', 50);
switch($this->params->get('subtitledisplay', '1')) {
case '1':
$category_field = 'category_name';
break;
case '2':
$category_field = 'customtitle';
break;
}
$search_product_description = (bool) $this->params->get('enable_product_description_search', TRUE);
$search_product_s_description = (bool) $this->params->get('enable_product_short_description_search', TRUE);
$search_customfields = (bool) $this->params->get('enable_customfields', TRUE);
$customfield_ids_condition = "";
if ($search_customfields) {
$value = trim($this->params->get('customfields', ""));
// Remove all spaces
$value = str_replace(' ', '', $value);
if (!empty($value)){
$customfield_ids = explode(",", $value);
// Make sure we have only integers
foreach($customfield_ids as &$id) {
$id = intval($id);
}
// The custom field ID must be either in the list specified or NULL.
$customfield_ids_condition = "AND cf.virtuemart_custom_id IN (" .
implode(',', $customfield_ids) . ")";
}
}
if (!class_exists('VmConfig')) {
// FIX THE MISSING DS ERROR ON JOOMLA 3 VM BETTER SEARCH PLUGIN : https://forum.virtuemart.net/index.php?topic=125681.0
defined('DS') or define('DS', DIRECTORY_SEPARATOR);
require(JPATH_ADMINISTRATOR . DS . 'components' . DS . 'com_virtuemart' . DS . 'helpers' . DS . 'config.php');
}
VmConfig::loadConfig();
$text = trim($text);
if (empty($text))
return array();
switch ($phrase) {
case 'exact':
$wheres2 = array();
// product_sku should be exact match
$text = $db->quote("%$text%", TRUE);
$wheres2[] = "p.product_sku LIKE $text";
$wheres2[] = "a.product_name LIKE $text";
$wheres2[] = "b.$category_field LIKE $text";
if ($search_product_s_description)
$wheres2[] = "a.product_s_desc LIKE $text";
if ($search_product_description)
$wheres2[] = "a.product_desc LIKE $text";
if ($search_customfields)
$wheres2[] = "(cf.customfield_value LIKE $text $customfield_ids_condition)";
$where = '(' . implode (') OR (', $wheres2) . ')';
break;
case 'all':
case 'any':
default:
$words = explode (' ', $text);
$wheres = array();
foreach ($words as $word) {
$wheres2 = array();
// product_sku should be exact match
$word = $db->quote("%$word%", TRUE);
$wheres2[] = "p.product_sku LIKE $word";
$wheres2[] = "a.product_name LIKE $word";
$wheres2[] = "b.$category_field LIKE $word";
if ($search_product_s_description)
$wheres2[] = "a.product_s_desc LIKE $word";
if ($search_product_description)
$wheres2[] = "a.product_desc LIKE $word";
if ($search_customfields)
$wheres2[] = "(cf.customfield_value LIKE $word $customfield_ids_condition)";
$wheres[] = implode (' OR ', $wheres2);
}
$where = '(' . implode (($phrase == 'all' ? ') AND (' : ') OR ('), $wheres) . ')';
break;
}
switch($ordering) {
case 'alpha':
$order = 'a.product_name ASC';
break;
case 'category':
$order = 'b.category_name ASC, a.product_name ASC';
break;
case 'popular':
$order = 'a.product_name ASC';
break;
case 'newest':
$order = 'p.created_on DESC';
break;
case 'oldest':
$order = 'p.created_on ASC';
break;
default:
$order = 'a.product_name ASC';
}
$shopper_group_condition="";
$currentVMuser = VmModel::getModel('user')->getUser();
$virtuemart_shoppergroup_ids = (array)$currentVMuser->shopper_groups;
if (is_array($virtuemart_shoppergroup_ids)) {
$sgrgroups = array();
foreach($virtuemart_shoppergroup_ids as $virtuemart_shoppergroup_id) {
$sgrgroups[] = 'psgr.`virtuemart_shoppergroup_id`= "' . (int)$virtuemart_shoppergroup_id . '" ';
}
$sgrgroups[] = 'psgr.`virtuemart_shoppergroup_id` IS NULL ';
$shopper_group_condition = " AND ( " . implode (' OR ', $sgrgroups) . " ) ";
}
$uncategorized_products_condition = VmConfig::get('show_uncat_child_products') ?
'' : ' AND b.virtuemart_category_id > 0 ';
$query = "
SELECT DISTINCT
a.product_name AS title,
a.product_s_desc AS text,
p.created_on as created,
p.published,
'2' AS browsernav,
(SELECT m.file_url AS path
FROM #__virtuemart_medias AS m
LEFT JOIN #__virtuemart_product_medias AS me ON m.virtuemart_media_id = me.virtuemart_media_id
WHERE me.virtuemart_product_id = a.virtuemart_product_id ORDER BY me.ordering ASC LIMIT 1 ) AS image,
GROUP_CONCAT(DISTINCT b.$category_field
ORDER BY b.$category_field SEPARATOR ', ') as section,
(SELECT pc2.virtuemart_category_id
FROM #__virtuemart_product_categories as pc2
WHERE pc2.virtuemart_product_id = a.virtuemart_product_id LIMIT 1) AS cat_id
FROM `#__virtuemart_products_" . VmConfig::$vmlang . "` AS a
JOIN #__virtuemart_products AS p USING (`virtuemart_product_id`)
LEFT JOIN `#__virtuemart_product_categories` AS xref
ON xref.`virtuemart_product_id` = a.`virtuemart_product_id`
LEFT JOIN `#__virtuemart_categories_" . VmConfig::$vmlang . "` AS b
ON b.`virtuemart_category_id` = xref.`virtuemart_category_id`
LEFT JOIN `#__virtuemart_product_shoppergroups` as `psgr`
ON (`psgr`.`virtuemart_product_id`=`a`.`virtuemart_product_id`)
LEFT JOIN `#__virtuemart_product_customfields` AS cf
ON cf.virtuemart_product_id = a.virtuemart_product_id
LEFT JOIN `#__virtuemart_customs` AS customs
ON customs.virtuemart_custom_id = cf.virtuemart_customfield_id
WHERE
($where)
AND p.published='1'
$shopper_group_condition
$uncategorized_products_condition
GROUP BY xref.virtuemart_product_id
ORDER BY $order";
$db->setQuery($query, 0, $limit);
//echo $query; exit;
$rows = $db->loadObjectList();
if ($rows) {
foreach ($rows as $key => $row) {
$rows[$key]->href = 'index.php?option=com_virtuemart&view=productdetails' .
// line below somehow changes search result links without changing title
$row->virtuemart_product_id . '&virtuemart_category_id=' . $row->cat_id;
$rows[$key]->price = $this->getPrice($row->virtuemart_product_id);
if($row->image != "" && (false === strpos($row->image, "stories"))) {
$rows[$key]->image = "images/stories/virtuemart/product/".$row->image;
}
}
}
return $rows;
}
function getPrice($pid) {
if (!class_exists('CurrencyDisplay')) {
require_once(JPATH_VM_ADMINISTRATOR . DS . 'helpers' . DS . 'currencydisplay.php');
}
$product_model = VmModel::getModel('product');
$currency = CurrencyDisplay::getInstance();
$product = $product_model->getProduct($pid,TRUE,TRUE,TRUE,1);
$p = str_replace("PricesalesPrice", "", $currency->createPriceDiv ('salesPrice', '', $product->prices));
return $p;
}
}
My psuedo-solution was to import the aforementioned virtuemart.php file from a vanilla Virtuemart download that matched the version I am using.
The file extracted was found in com_virtuemart.3.0.14_ext_aio.zip: /admin/plugins/search/virtuemart/virtuemart.php.
Renamed original post php file to .bak and dropped this one in the same location. Results are now behaving like stock Joomla search (sans all custom styling).

Is it possible to do paging with JoinSqlBuilder?

I have a pretty normal join that I create via JoinSqlBuilder
var joinSqlBuilder = new JoinSqlBuilder<ProductWithManufacturer, Product>()
.Join<Product, Manufacturer>(sourceColumn: p => p.ManufacturerId,
destinationColumn: mf => mf.Id,
sourceTableColumnSelection: p => new { ProductId = p.Id, ProductName = p.Name },
destinationTableColumnSelection: m => new { ManufacturerId = m.Id, ManufacturerName = m.Name })
Of course, the join created by this could potentially return a lot of rows, so I want to use paging - preferably on the server-side. However, I cannot find anything in the JoinSqlBuilder which would let me do this? Am I missing something or does JoinSqlBuilder not have support for this (yet)?
If you aren't using MS SQL Server I think the following will work.
var sql = joinSqlBuilder.ToSql();
var data = this.Select<ProductWithManufacturer>(
q => q.Select(sql)
.Limit(skip,rows)
);
If you are working with MS SQL Server, it will most likely blow up on you. I am working to merge a more elegant solution similar to this into JoinSqlBuilder. The following is a quick and dirty method to accomplish what you want.
I created the following extension class:
public static class Extension
{
private static string ToSqlWithPaging<TResult, TTarget>(
this JoinSqlBuilder<TResult, TTarget> bldr,
string orderColumnName,
int limit,
int skip)
{
var sql = bldr.ToSql();
return string.Format(#"
SELECT * FROM (
SELECT ROW_NUMBER() OVER (ORDER BY [{0}]) As RowNum, *
FROM (
{1}
)as InnerResult
)as RowConstrainedResult
WHERE RowNum > {2} AND RowNum <= {3}
", orderColumnName, sql, skip, skip + limit);
}
public static string ToSqlWithPaging<TResult, TTarget>(
this JoinSqlBuilder<TResult, TTarget> bldr,
Expression<Func<TResult, object>> orderSelector,
int limit,
int skip)
{
var member = orderSelector.Body as MemberExpression;
if (member == null)
throw new ArgumentException(
"TResult selector refers to a non member."
);
var propInfo = member.Member as PropertyInfo;
if (propInfo == null)
throw new ArgumentException(
"TResult selector refers to a field, it must be a property."
);
var orderSelectorName = propInfo.Name;
return ToSqlWithPaging(bldr, orderSelectorName, limit, skip);
}
}
It is applied as follows:
List<Entity> GetAllEntities(int limit, int skip)
{
var bldr = GetJoinSqlBuilderFor<Entity>();
var sql = bldr.ToSqlWithPaging(
entity => entity.Id,
limit,
skip);
return this.Db.Select<Entity>(sql);
}

Excluding all categories except one from indexing - joomla smart search

I would like to have joomla automatically re-index all the articles that are in a certain category (and subcategory) without indexing the rest. I am not looking for a cron job. I want joomla to do this when I hit the indexing button.
In other words, all the articles that are not in that particular category or its child subcategories should not be indexed and thus not show up in the search results.
EDIT (more info): This is for a specific site where I know the category id(s). I should probably also add that it is a bilingual site, the search module is appearing and indexing twice on the same page for each language. I.e. it indexes and appears on the English 'blog' page and the German 'blog' page.
Is this possible or do I need to manually delete the indexed files I don't want to show up?
EDIT: The discover tool is not working, here is the xml file to try and find out why.
<?xml version="1.0" encoding="utf-8"?>
<extension version="3.1" type="plugin" group="finder" method="upgrade">
<name>plg_finder_content</name>
<author>Joomla! Project</author>
<creationDate>August 2011</creationDate>
<copyright>(C) 2005 - 2014 Open Source Matters. All rights reserved.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin#joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.0.0</version>
<description>PLG_FINDER_CONTENT_XML_DESCRIPTION</description>
<scriptfile>script.php</scriptfile>
<files>
<file plugin="content">article_selectedcategories.php</file>
<filename>index.html</filename>
</files>
<languages>
<language tag="en-GB">language/en-GB/en-GB.plg_finder_content.ini</language>
<language tag="en-GB">language/en-GB/en-GB.plg_finder_content.sys.ini</language>
</languages>
</extension>
EDIT The code from the php to find out why it indexes everything and not just category 31.
<?php
/**
* #package Joomla.Plugin
* #subpackage Finder.Content
*
* #copyright Copyright (C) 2005 - 2014 Open Source Matters, Inc. All rights reserved.
* #license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_BASE') or die;
require_once JPATH_ADMINISTRATOR . '/components/com_finder/helpers/indexer/adapter.php';
/**
* Smart Search adapter for com_content.
*
* #package Joomla.Plugin
* #subpackage Finder.Content
* #since 2.5
*/
class PlgFinderContent extends FinderIndexerAdapter
{
/**
* The plugin identifier.
*
* #var string
* #since 2.5
*/
protected $context = 'Content';
/**
* The extension name.
*
* #var string
* #since 2.5
*/
protected $extension = 'com_content';
/**
* The sublayout to use when rendering the results.
*
* #var string
* #since 2.5
*/
protected $layout = 'article';
/**
* The type of content that the adapter indexes.
*
* #var string
* #since 2.5
*/
protected $type_title = 'Article';
/**
* The table name.
*
* #var string
* #since 2.5
*/
protected $table = '#__content';
/**
* Load the language file on instantiation.
*
* #var boolean
* #since 3.1
*/
protected $autoloadLanguage = true;
/**
* Method to update the item link information when the item category is
* changed. This is fired when the item category is published or unpublished
* from the list view.
*
* #param string $extension The extension whose category has been updated.
* #param array $pks A list of primary key ids of the content that has changed state.
* #param integer $value The value of the state that the content has been changed to.
*
* #return void
*
* #since 2.5
*/
public function onFinderCategoryChangeState($extension, $pks, $value)
{
// Make sure we're handling com_content categories.
if ($extension == 'com_content')
{
$this->categoryStateChange($pks, $value);
}
}
/**
* Method to remove the link information for items that have been deleted.
*
* #param string $context The context of the action being performed.
* #param JTable $table A JTable object containing the record to be deleted
*
* #return boolean True on success.
*
* #since 2.5
* #throws Exception on database error.
*/
public function onFinderAfterDelete($context, $table)
{
if ($context == 'com_content.article')
{
$id = $table->id;
}
elseif ($context == 'com_finder.index')
{
$id = $table->link_id;
}
else
{
return true;
}
// Remove item from the index.
return $this->remove($id);
}
/**
* Smart Search after save content method.
* Reindexes the link information for an article that has been saved.
* It also makes adjustments if the access level of an item or the
* category to which it belongs has changed.
*
* #param string $context The context of the content passed to the plugin.
* #param JTable $row A JTable object.
* #param boolean $isNew True if the content has just been created.
*
* #return boolean True on success.
*
* #since 2.5
* #throws Exception on database error.
*/
public function onFinderAfterSave($context, $row, $isNew)
{
// We only want to handle articles here.
if ($context == 'com_content.article' || $context == 'com_content.form')
{
// Check if the access levels are different.
if (!$isNew && $this->old_access != $row->access)
{
// Process the change.
$this->itemAccessChange($row);
}
// Reindex the item.
$this->reindex($row->id);
}
// Check for access changes in the category.
if ($context == 'com_categories.category')
{
// Check if the access levels are different.
if (!$isNew && $this->old_cataccess != $row->access)
{
$this->categoryAccessChange($row);
}
}
return true;
}
/**
* Smart Search before content save method.
* This event is fired before the data is actually saved.
*
* #param string $context The context of the content passed to the plugin.
* #param JTable $row A JTable object.
* #param boolean $isNew If the content is just about to be created.
*
* #return boolean True on success.
*
* #since 2.5
* #throws Exception on database error.
*/
public function onFinderBeforeSave($context, $row, $isNew)
{
// We only want to handle articles here.
if ($context == 'com_content.article' || $context == 'com_content.form')
{
// Query the database for the old access level if the item isn't new.
if (!$isNew)
{
$this->checkItemAccess($row);
}
}
// Check for access levels from the category.
if ($context == 'com_categories.category')
{
// Query the database for the old access level if the item isn't new.
if (!$isNew)
{
$this->checkCategoryAccess($row);
}
}
return true;
}
/**
* Method to update the link information for items that have been changed
* from outside the edit screen. This is fired when the item is published,
* unpublished, archived, or unarchived from the list view.
*
* #param string $context The context for the content passed to the plugin.
* #param array $pks An array of primary key ids of the content that has changed state.
* #param integer $value The value of the state that the content has been changed to.
*
* #return void
*
* #since 2.5
*/
public function onFinderChangeState($context, $pks, $value)
{
// We only want to handle articles here.
if ($context == 'com_content.article' || $context == 'com_content.form')
{
$this->itemStateChange($pks, $value);
}
// Handle when the plugin is disabled.
if ($context == 'com_plugins.plugin' && $value === 0)
{
$this->pluginDisable($pks);
}
}
/**
* Method to index an item. The item must be a FinderIndexerResult object.
*
* #param FinderIndexerResult $item The item to index as an FinderIndexerResult object.
* #param string $format The item format. Not used.
*
* #return void
*
* #since 2.5
* #throws Exception on database error.
*/
protected function index(FinderIndexerResult $item, $format = 'html')
{
$item->setLanguage();
// Check if the extension is enabled.
if (JComponentHelper::isEnabled($this->extension) == false)
{
return;
}
// Initialise the item parameters.
$registry = new JRegistry;
$registry->loadString($item->params);
$item->params = JComponentHelper::getParams('com_content', true);
$item->params->merge($registry);
$registry = new JRegistry;
$registry->loadString($item->metadata);
$item->metadata = $registry;
// Trigger the onContentPrepare event.
$item->summary = FinderIndexerHelper::prepareContent($item->summary, $item->params);
$item->body = FinderIndexerHelper::prepareContent($item->body, $item->params);
// Build the necessary route and path information.
$item->url = $this->getURL($item->id, $this->extension, $this->layout);
$item->route = ContentHelperRoute::getArticleRoute($item->slug, $item->catslug, $item->language);
$item->path = FinderIndexerHelper::getContentPath($item->route);
// Get the menu title if it exists.
$title = $this->getItemMenuTitle($item->url);
// Adjust the title if necessary.
if (!empty($title) && $this->params->get('use_menu_title', true))
{
$item->title = $title;
}
// Add the meta-author.
$item->metaauthor = $item->metadata->get('author');
// Add the meta-data processing instructions.
$item->addInstruction(FinderIndexer::META_CONTEXT, 'metakey');
$item->addInstruction(FinderIndexer::META_CONTEXT, 'metadesc');
$item->addInstruction(FinderIndexer::META_CONTEXT, 'metaauthor');
$item->addInstruction(FinderIndexer::META_CONTEXT, 'author');
$item->addInstruction(FinderIndexer::META_CONTEXT, 'created_by_alias');
// Translate the state. Articles should only be published if the category is published.
$item->state = $this->translateState($item->state, $item->cat_state);
// Add the type taxonomy data.
$item->addTaxonomy('Type', 'Article');
// Add the author taxonomy data.
if (!empty($item->author) || !empty($item->created_by_alias))
{
$item->addTaxonomy('Author', !empty($item->created_by_alias) ? $item->created_by_alias : $item->author);
}
// Add the category taxonomy data.
$item->addTaxonomy('Category', $item->category, $item->cat_state, $item->cat_access);
// Add the language taxonomy data.
$item->addTaxonomy('Language', $item->language);
// Get content extras.
FinderIndexerHelper::getContentExtras($item);
// Index the item.
$this->indexer->index($item);
}
/**
* Method to setup the indexer to be run.
*
* #return boolean True on success.
*
* #since 2.5
*/
protected function setup()
{
// Load dependent classes.
include_once JPATH_SITE . '/components/com_content/helpers/route.php';
return true;
}
/**
* Method to get the SQL query used to retrieve the list of content items.
*
* #param mixed $query A JDatabaseQuery object or null.
*
* #return JDatabaseQuery A database object.
*
* #since 2.5
*/
protected function getListQuery($query = null)
{
$db = JFactory::getDbo();
// Check if we can use the supplied SQL query.
$query = $query instanceof JDatabaseQuery ? $query : $db->getQuery(true)
->select('a.id, a.title, a.alias, a.introtext AS summary, a.fulltext AS body')
->select('a.state, a.catid, a.created AS start_date, a.created_by')
->select('a.created_by_alias, a.modified, a.modified_by, a.attribs AS params')
->select('a.metakey, a.metadesc, a.metadata, a.language, a.access, a.version, a.ordering')
->select('a.publish_up AS publish_start_date, a.publish_down AS publish_end_date')
->select('c.title AS category, c.published AS cat_state, c.access AS cat_access');
$query->where('a.catid = 31');
// Handle the alias CASE WHEN portion of the query
$case_when_item_alias = ' CASE WHEN ';
$case_when_item_alias .= $query->charLength('a.alias', '!=', '0');
$case_when_item_alias .= ' THEN ';
$a_id = $query->castAsChar('a.id');
$case_when_item_alias .= $query->concatenate(array($a_id, 'a.alias'), ':');
$case_when_item_alias .= ' ELSE ';
$case_when_item_alias .= $a_id.' END as slug';
$query->select($case_when_item_alias);
$case_when_category_alias = ' CASE WHEN ';
$case_when_category_alias .= $query->charLength('c.alias', '!=', '0');
$case_when_category_alias .= ' THEN ';
$c_id = $query->castAsChar('c.id');
$case_when_category_alias .= $query->concatenate(array($c_id, 'c.alias'), ':');
$case_when_category_alias .= ' ELSE ';
$case_when_category_alias .= $c_id.' END as catslug';
$query->select($case_when_category_alias)
->select('u.name AS author')
->from('#__content AS a')
->join('LEFT', '#__categories AS c ON c.id = a.catid')
->join('LEFT', '#__users AS u ON u.id = a.created_by');
return $query;
}
}
I added the whole thing, just search for "where" to find the part that I changed. Everything else I did not touch.
Oh I think you are looking at the xml not the php.
// Check if we can use the supplied SQL query.
$query = $query instanceof JDatabaseQuery ? $query : $db->getQuery(true)
->select('a.id, a.title, a.alias, a.introtext AS summary, a.fulltext AS body')
->select('a.state, a.catid, a.created AS start_date, a.created_by')
->select('a.created_by_alias, a.modified, a.modified_by, a.attribs AS params')
->select('a.metakey, a.metadesc, a.metadata, a.language, a.access, a.version, a.ordering')
->select('a.publish_up AS publish_start_date, a.publish_down AS publish_end_date')
->select('c.title AS category, c.published AS cat_state, c.access AS cat_access');
If you want to exclude category 7 add
$query->where('a.catid <> 7');
after the last select.
Update ... or if you only want catgory 7
$query->where('a.catid = 7');
or if you only want category 7 and its children you would use IN ( comma separated list ).
Update
so this is what i'm getting for the generated query
SELECT a.id, a.title, a.alias, a.introtext AS summary, a.fulltext AS body,a.state, a.catid, a.created AS start_date, a.created_by,a.created_by_alias, a.modified, a.modified_by, a.attribs AS params,a.metakey, a.metadesc, a.metadata, a.language, a.access, a.version, a.ordering,a.publish_up AS publish_start_date, a.publish_down AS publish_end_date,c.title AS category, c.published AS cat_state, c.access AS cat_access, CASE WHEN CHAR_LENGTH(a.alias) != 0 THEN CONCAT_WS(':', a.id, a.alias) ELSE a.id END as slug, CASE WHEN CHAR_LENGTH(c.alias) != 0 THEN CONCAT_WS(':', c.id, c.alias) ELSE c.id END as catslug,u.name AS author
FROM a1pbr_content AS a
LEFT JOIN a1pbr_categories AS c ON c.id = a.catid
LEFT JOIN a1pbr_users AS u ON u.id = a.created_by
WHERE a.catid = 31
And that does give me just articles with catid of 31.
WHen you are indexing are you using the index button or doing it on save?

Acceptable to save results from Spotify search API?

Reading the TOS for the Spotify web API, developers are not allowed to aggregate data from the API in creation of databases. I don't know if what I'm trying to accomplish counts as "aggregation."
I have a website that allows users to make suggestions for songs to be played at a wedding. I let them put in song name, artist, and album names so that the DJ can readily find the music. All this is user supplied. The songs are then approved by the bride/groom and voted on by other guests to generate a playlist for the DJ will know what music will be popular at the event.
What I want to provide is a way for the user to use that information and have the ability to search through the top few search results on Spotify, select the right track, and associate the Spotify track with their suggestion. This let's other guests hear the song they are suggesting if they're not familiar with it and allows the administrators to allow or disallow the song depending on the tastes of the bride/groom.
In order to avoid API calls exceeding the rate limit, I'd like to be able to store the Spotify URI returned from the search results with the user supplied song information so that I can generate a play button on the site for the songs suggested.
Does this count as aggregation, or would this be allowed under the current TOS for the web search API?
What you're doing sounds just fine.
The TOS section you're asking about is to prevent people making automated tools that scrape the Spotify catalogue without user interaction. If you're writing a "normal" application and caching data from the Spotify APIs as a result of a user actually doing something like searching, browsing, etc etc you have no problems.
Source: I work at Spotify.
I use this one:
<?php
namespace App\Services;
use DB;
use Exception;
use App\Genre;
use App\Album;
use App\Artist;
use Illuminate\Support\Str;
class ArtistSaver {
/**
* Save artist to database and return it.
*
* #param array $data
* #return Artist
*/
public function save($data)
{
$artist = Artist::whereName($data['mainInfo']['name'])->first();
if ( ! $artist) {
$artist = Artist::create($data['mainInfo']);
} else {
$artist->fill($data['mainInfo'])->save();
}
$this->saveAlbums($data, $artist);
if (isset($data['albums'])) {
$this->saveTracks($data['albums'], $artist);
}
if (isset($data['similar'])) {
$this->saveSimilar($data['similar'], $artist);
}
if (isset($data['genres']) && ! empty($data['genres'])) {
$this->saveGenres($data['genres'], $artist);
}
return $artist;
}
/**
* Save and attach artist genres.
*
* #param array $genres
* #param Artist $artist
*/
public function saveGenres($genres, $artist) {
$existing = Genre::whereIn('name', $genres)->get();
$ids = [];
foreach($genres as $genre) {
$dbGenre = $existing->filter(function($item) use($genre) { return $item->name === $genre; })->first();
//genre doesn't exist in db yet, so we need to insert it
if ( ! $dbGenre) {
try {
$dbGenre = Genre::create(['name' => $genre]);
} catch(Exception $e) {
continue;
}
}
$ids[] = $dbGenre->id;
}
//attach genres to artist
$artist->genres()->sync($ids, false);
}
/**
* Save artists similar artists to database.
*
* #param $similar
* #param $artist
* #return void
*/
public function saveSimilar($similar, $artist)
{
$names = array_map(function($item) { return $item['name']; }, $similar);
//insert similar artists that don't exist in db yet
$this->saveOrUpdate($similar, array_flatten($similar), 'artists');
//get ids in database for artist we just inserted
$ids = Artist::whereIn('name', $names)->lists('id');
//attach ids to given artist
$artist->similar()->sync($ids);
}
/**
* Save artist albums to database.
*
* #param array $data
* #param Artist|null $artist
* $param int|null
* #return void
*/
public function saveAlbums($data, $artist = null, $albumId = null)
{
if (isset($data['albums']) && count($data['albums'])) {
$b = $this->prepareAlbumBindings($data['albums'], $artist, $albumId);
$this->saveOrUpdate($b['values'], $b['bindings'], 'albums');
}
}
/**
* Save albums tracks to database.
*
* #param array $albums
* #param Artist|null $artist
* #param Album|null $trackAlbum
* #return void
*/
public function saveTracks($albums, $artist, $tracksAlbum = null)
{
if ( ! $albums || ! count($albums)) return;
$tracks = [];
foreach($albums as $album) {
if ( ! isset($album['tracks']) || empty($album['tracks'])) continue;
if ($tracksAlbum) {
$id = $tracksAlbum['id'];
} else {
$id = $this->getIdFromAlbumsArray($album['name'], $artist['albums']);
}
foreach($album['tracks'] as $track) {
$track['album_id'] = $id;
$tracks[] = $track;
}
}
if ( ! empty($tracks)) {
$this->saveOrUpdate($tracks, array_flatten($tracks), 'tracks');
}
}
private function getIdFromAlbumsArray($name, $albums) {
$id = false;
foreach($albums as $album) {
if ($name === $album['name']) {
$id = $album['id']; break;
}
}
if ( ! $id) {
foreach($albums as $album) {
if (Str::slug($name) == Str::slug($album['name'])) {
$id = $album['id']; break;
}
}
}
return $id;
}
/**
* Unset tracks key from album arrays and flatten them into single array.
*
* #param array $albums
* #param Artist|null $artist
* #param int|null $albumId
* #return array
*/
private function prepareAlbumBindings($albums, $artist = null, $albumId = null)
{
$flat = [];
foreach($albums as $k => $album) {
if (isset($albums[$k]['tracks'])) unset($albums[$k]['tracks']);
if ( ! isset($albums[$k]['artist_id']) || ! $albums[$k]['artist_id']) {
$albums[$k]['artist_id'] = $artist ? $artist->id : 0;
}
//can't insert null into auto incrementing id because
//mysql will increment the id instead of keeping the old one
if ($albumId) {
$albums[$k]['id'] = $albumId;
}
foreach($albums[$k] as $name => $data) {
if ($name !== 'tracks') {
$flat[] = $data;
}
}
}
return ['values' => $albums, 'bindings' => $flat];
}
/**
* Compiles insert on duplicate update query for multiple inserts.
*
* #param array $values
* #param array $bindings
* #param string $table
*
* #return void
*/
public function saveOrUpdate(array $values, $bindings, $table)
{
if (empty($values)) return;
$first = head($values);
//count how many inserts we need to make
$amount = count($values);
//count in how many columns we're inserting
$columns = array_fill(0, count($first), '?');
$columns = '(' . implode(', ', $columns) . ') ';
//make placeholders for the amount of inserts we're doing
$placeholders = array_fill(0, $amount, $columns);
$placeholders = implode(',', $placeholders);
$updates = [];
//construct update part of the query if we're trying to insert duplicates
foreach ($first as $column => $value) {
$updates[] = "$column = COALESCE(values($column), $column)";
}
$prefixed = DB::getTablePrefix() ? DB::getTablePrefix().$table : $table;
$query = "INSERT INTO {$prefixed} " . '(' . implode(',' , array_keys($first)) . ')' . ' VALUES ' . $placeholders .
'ON DUPLICATE KEY UPDATE ' . implode(', ', $updates);
DB::statement($query, $bindings);
}
}

Cassandra is not retrieving the correct integer value

I am using cql 3.0.0
I have executed the query:
INSERT INTO emp (empID, deptID, first_name, last_name)
VALUES (104, 15, 'jane', 'smith')
On retrieving this record, I get the following values:
empid = h
deptid = (blank value)
first_name = 'jane'
last_name = 'smith'
On searching for it, I found that h is equivalent to utf-8 character 104. Also 15 in utf-8 is blank.
(Reference Link: http://www.utf8-chartable.de/unicode-utf8-table.pl?utf8=dec&unicodeinhtml=dec )
I have set the column types to int during create table, but on retrieving I am not getting the int values.
How do I get the correct values to be retrieved. I do not want the utf-8 values.
Thanks
I am using cassandra 1.2.4
Below is my code written in phpcassa:
require_once(__DIR__.'/../lib/autoload.php');
use phpcassa\Connection\ConnectionPool;
use phpcassa\ColumnFamily;
use phpcassa\SystemManager;
use phpcassa\Schema\StrategyClass;
use cassandra\Compression;
use cassandra\ConsistencyLevel;
$pool = new ConnectionPool("prod",array('X.X.X.X','X.X.X.X'));
$raw = $pool->get();
$rows = $raw->client->set_cql_version("3.0.0");
$rows = $raw->client->execute_cql3_query('USE prod;', Compression::NONE, ConsistencyLevel::ONE );
$rows = $raw->client->execute_cql3_query('CREATE TABLE emp (
empID int,
deptID int,
first_name varchar,
last_name varchar,
PRIMARY KEY (empID, deptID));
', Compression::NONE, ConsistencyLevel::ONE );
$rows = $raw->client->execute_cql3_query('INSERT INTO emp (empID, deptID, first_name, last_name)
VALUES (104, 15, \'jane\', \'smith\');
', Compression::NONE, ConsistencyLevel::ONE );
$rows = $raw->client->execute_cql3_query('SELECT * FROM emp WHERE empID IN (2,104) ORDER BY deptID ASC;', Compression::NONE, ConsistencyLevel::ONE );
echo "<pre>";
print_r($rows);
echo "<pre>";
The output generated is:
cassandra\CqlRow Object
(
[key] =>
[columns] => Array
(
[0] => cassandra\Column Object
(
[name] => empid
[value] => h
[timestamp] =>
[ttl] =>
)
[1] => cassandra\Column Object
(
[name] => deptid
[value] =>
[timestamp] =>
[ttl] =>
)
[2] => cassandra\Column Object
(
[name] => first_name
[value] => jane
[timestamp] =>
[ttl] =>
)
[3] => cassandra\Column Object
(
[name] => last_name
[value] => smith
[timestamp] =>
[ttl] =>
)
)
)
Though a bit late answer, but I was desperately searching for the solution while writing my own cassandra-wrapper for Yii framework. I came up with something like the following class that eliminates the problem of incorrect binary to different data types in cassandra including integer types:
Yii::import('ext.phpcassa.autoload');
require_once('protected/extensions/phpcassa/autoload.php');
use phpcassa\Connection\ConnectionPool;
use phpcassa\ColumnFamily;
use phpcassa\ColumnSlice;
use phpcassa\SystemManager;
use phpcassa\Schema\StrategyClass;
use phpcassa\Schema\DataType;
class ACassandraConnection extends CApplicationComponent {
protected $_pool = null;
public $keyspace = null;
public $servers = null;
/**
* Establish connection to cassandra cluster
* #return object
*/
private function _get_raw() {
if ($this->_pool === null) {
$this->_pool = new ConnectionPool($this->keyspace, $this->servers);
}
return $this->_pool->get();
}
public function cql_query($query) {
$raw = $this->_get_raw();
$cql_result = $raw->client->execute_cql3_query($query, cassandra\Compression::NONE, cassandra\ConsistencyLevel::ONE);
$this->_pool->return_connection($raw);
return $cql_result;
}
public function cql_get_rows($cql_result) {
if ($cql_result->type == 1) {
$rows = array();
foreach ($cql_result->rows as $rowIndex => $cqlRow) {
$cols = array();
foreach ($cqlRow->columns as $colIndex => $column) {
$type = DataType::get_type_for($cql_result->schema->value_types[$column->name]);
$cols[$column->name] = $type->unpack($column->value);
}
$rows[] = $cols;
}
return $rows;
} else {
return null;
}
}
/**
* Perform garbage collection
*/
public function __destruct() {
if($this->_pool !== null) {
$this->_pool->close();
}
}
}
I will say please review your solution, i have tried exactly the same thing you described as your problem, but its working normal for me. Might be at the time of creation you have used utf-8 as their type.
Schema
CREATE COLUMNFAMILY test(empID int, deptID int, first_name text, last_name text, PRIMARY KEY(empID));
Insert
INSERT INTO emp (empID, deptID, first_name, last_name) VALUES (104, 15, 'jane', 'smith');
Retrieve
SELECT * FROM test ;
empid | deptid | first_name | last_name
-------+--------+------------+-----------
104 | 15 | jane | smith

Resources