Optimizing PyroCMS code for Keywords - codeigniter

I built this method following the the tagged() method from blog module
public function genres($genre = null)
{
$this->db->order_by('name', 'ASC');
$result = $this->db->get('keywords');
$genres = $result->result();
if($genre)
{
$this->load->model('genres_m');
// decode encoded cyrillic characters
$genre = rawurldecode($genre) OR redirect('generos');
$time[] = time();
// Count total blog posts and work out how many pages exist
$pagination = create_pagination(lang('ebooks:routes:genres') . '/' . $genre, $this->genres_m->count_genres_by($genre, array('entry_active' => 1)), NULL, 4);
$time[] = time();
// Get the current page of blog posts
$books = $this->genres_m
->limit($pagination['per_page'])
->order_by('info_title', 'ASC')
->get_genres_by($genre, array('entry_active' => 1));
$time[] = time();
foreach ($books AS &$book)
{
$book->books_info_genre = Keywords::get($book->books_info_genre, 'blog/tagged');
$book->url = site_url(lang('ebooks:routes:ebook') . '/' . $book->info_title . '/' . $book->id);
}
$time[] = time();
// Set meta description based on post titles
//$meta = $this->_posts_metadata($books);
$name = str_replace('-', ' ', $genre);
// Build the page
$this->template
->title($this->_template_title(lang('ebooks:of').' '.$name))
->set_metadata('description', $this->_template_title(lang('ebooks:of').' '.$name))
->set_metadata('keywords', $this->_template_title(lang('ebooks:of').' '.$name))
->set('genres', $genres)
->set('books', $books)
->set('genre', $genre)
->set('time', $time)
->set('pagination', $pagination)
->build('genres-list');
}
else
{
$this->template->title($this->_template_title(lang('ebooks:genres_by')))
->set_metadata('description', $this->_template_title(lang('ebooks:genres_by')))
->set_metadata('keywords', $this->_template_title(lang('ebooks:genres_by')))
->set('genres', $genres)
->build('genres-list');
}
}
And, this is the model:
public function count_genres_by($genre, $params)
{
return $this->db->select('*')
->from('downloads_books_book_info')
->join('keywords_applied', 'keywords_applied.hash = downloads_books_book_info.books_info_genre')
->join('keywords', 'keywords.id = keywords_applied.keyword_id')
->where('keywords.name', str_replace('-', ' ', $genre))
->where($params)
->count_all_results();
}
public function get_genres_by($genre, $params)
{
return $this->db->select('*')
->from('downloads_books_book_info')
->join('keywords_applied', 'keywords_applied.hash = downloads_books_book_info.books_info_genre')
->join('keywords', 'keywords.id = keywords_applied.keyword_id')
->where('keywords.name', str_replace('-', ' ', $genre))
->where($params)
->get()
->result();
}
As you can see in the first part of code, I got the time() four times, giving the delays:
18:49 - 19:03 - 19:41 - 19:41
I have a DB with about 5K entries. How can I optimize this code?

You can use 2.2.0-beta1 and take advantage of the Search system, which will store all keywords as text and knock out a few joins for your queries.
Otherwise you can build your own index table using blog events, which will store keywords next to blog id's.
The main problem is that your are running an SQL query on EVERY single returned item, whilst it would be quicker to work out all the keywords in one go. Even a SQL sub-query would be slightly quicker.

Related

joomla - router change url when getting the name of product

I have build my own component in joomla and client wants now a friendly urls f.e
website.com/someplace/{product-id}-{product-name}. So i Build my own router like this.
function componentBuildRoute(&$query)
{
$segments = [];
if (isset($query['view'])) {
$segments[] = "szkolenie";
unset($query['view']);
}
if (isset($query['product_id'])) {
$productName = JFilterOutput::stringURLSafe(strtolower(getProductName($query['product_id'])));
$newName = $query['product_id'] . '-' . $productName;
$segments[] = $newName;
unset($query['product_id']);
}
return $segments;
}
and parse route function
function componentParseRoute($segments)
{
$app = JFactory::getApplication();
$menu = $app->getMenu();
$item =& $menu->getActive();
$count = count($segments);
switch ($item->query['view']) {
case 'catalogue' : {
$view = 'training';
$id = $segments[1];
}
break;
}
$data = [
'view' => $view,
'product_id' => $id
];
return $data;
}
While on the end of buildroute function segments are ok I have exactly what I want that on the beginning of parse route I have something like
website.com/szkolenie/1-krakow <-- I dont know wtf is this krakow( I know it is city i Poland) but still where is it get from ? The getProductName function implementation is
function getProductName($productId)
{
$db = JFactory::getDbo();
$query = $db->getQuery(true);
$query->select('#__component_training.id as id, #__component_product' . name)
->from($db->quoteName('#__component_training'))
->where('#__s4edu_product.product_id = ' . $productId)
->leftJoin('#__component_product ON
#__component_training.product_id=#__component_product.product_id');
$training = $db->loadObject();
return trim($training->name);
}
So taking all this into consideration I think that something is happening between the buildRoute and parseRoute, something what filters the $segment[1] variable, but how to disable that and why is it happening ?
P.S
Please do not send me to https://docs.joomla.org/Joomla_Routes_%26_SEF
I already know all the tutorials on joomla website which contains anything with sef.
P.S.S
It is built on joomla 3.7.0
You do not have a product named "krakow" ?
If not you can try to remove the $productName from the build function, just to check if this "krakow" is added automaticaly or it's from the getProductName() function.
Also i noticed that you have an error i guess in the function getProductName()
->where('#__s4edu_product.product_id = ' . $productId)
It's should be
->where('#__component_product.product_id = ' . $productId)

Using JFactory::getDbo()->insertObject with on duplicate key update

How to use:
JFactory::getDbo()->insertObject('#__card_bonus', $object);
with on duplicate key update ?
You have a few options:
1) Check for an entity id. This is my preferred option, because it only uses a single query, is reusable for any object, and is database agnostic - meaning it will work on whichever DBMS you choose, whereas the other two options are exclusive to MySQL.
if (isset($object->id)) {
$db->updateObject('#__card_bonus', $object);
}
else {
$db->insertObject('#__card_bonus', $object, 'id');
}
I often create an abstract model with a save(stdClass $object) method that does this check so I don't have to duplicate it.
2) Write your own query using the MySQL ON DUPLICATE KEY UPDATE syntax, which is a proprietary extension to the SQL standard, that you have demonstrated understanding of.
3) Write your own query using MySQL's proprietary REPLACE INTO extension.
<?php
$jarticle = new stdClass();
$jarticle->id = 1544;
$jarticle->title = 'New article';
$jarticle->alias = JFilterOutput::stringURLSafe($jarticle->title);
$jarticle->introtext = '<p>re</p>';
$jarticle->state = 1;
$jarticle->catid = 13;
$jarticle->created_by = 111;
$jarticle->access = 1;
$jarticle->language = '*';
$db = JFactory::getDbo();
try {
$query = $db->getQuery(true);
$result = JFactory::getDbo()->insertObject('#__content', $jarticle);
}
catch (Exception $e){
$result = JFactory::getDbo()->updateObject('#__content', $jarticle, 'id');
}
I use this method - are not fully satisfied, but ...
or for not object method:
$query = $db->getQuery(true);
$columns = array('username', 'password');
$values = array($db->quote($username), $db->quote($password));
$query
->insert($db->quoteName('#__db_name'))
->columns($db->quoteName($columns))
->values(implode(',', $values));
$query .= ' ON DUPLICATE KEY UPDATE ' . $db->quoteName('password') . ' = ' . $db->quote($password);
$db->setQuery($query);
JFactory::getDbo()->insertObject('#__card_bonus', $object, $keyName);
The name of the primary key. If provided the object property is updated.
Joomla doc ...

Codeigniter pagination links dosnt work after the first page

i have a search field and i want to show the results with pagination.
every thing works well in first result page but in other pages there is noting to show without any errors and when back to first page also there is noting.
i putted the posted key in session to avoid losing it.
but still noting.
this is my controller:
public function search()
{
$this->session->set_userdata('searched',$this->input->post('searchterm'));
$searchterm = $this->session->userdata('searched');
$limit = ($this->uri->segment(3) > 0)?$this->uri->segment(3):0;
$config['base_url'] = base_url() . 'home/search';
$config['total_rows'] = $this->home_model->search_record_count($searchterm,$language);
$config['per_page'] = 10;
$config['uri_segment'] = 3;
$config['display_pages'] = TRUE;
$choice = $config['total_rows']/$config['per_page'];
$config['num_links'] = 3;
$this->pagination->initialize($config);
$data['results'] = $this->home_model->search($searchterm,$limit,$language);
$data['links'] = $this->pagination->create_links();
$data['searchterm'] = $searchterm;
$data['total']= $this->home_model->search_record_count($searchterm,$language);
putHeader();
putTop();
putTopmenu();
putSearch($data);
putFooter();
}
this is my model:
public function search_record_count($searchterm)
{
$sql = "SELECT COUNT(*) As cnt FROM content WHERE body LIKE '%" . $searchterm . "%'";
$q = $this->db->query($sql);
$row = $q->row();
return $row->cnt;
}
public function search($searchterm,$limit)
{
$data = $this->db
->select('content.title,content.id,content.category,content.body,path')
->from('content')
->join('categories','noor_content.category = categories.id')
->like('noor_content.title', $searchterm)
->like('noor_content.body', $searchterm)
->limit("5")
->order_by("content.id","DESC")
->get();
if($data->num_rows() > 0)
{
return $data->result();
//print_r($data);exit();
}
else
{
return 0;
}
}
The problem is that you are not limiting your query according to the pagination. In your controller you are getting the 3rd URL segment
$limit = ($this->uri->segment(3) > 0)?$this->uri->segment(3):0;
And passing it to you model but then you are not doing anything with it. In your model you want to do something like
$data = $this->db
->select('content.title,content.id,content.category,content.body,path')
->from('content')
->join('categories','noor_content.category = categories.id')
->like('noor_content.title', $searchterm)
->like('noor_content.body', $searchterm)
->limit("10, " . $limit) //Edited this line
->order_by("content.id","DESC")
->get();
i think you should use method = 'get' instead of post... i mean some thing like this ..
<form method="get" action="your_controller/your_function">
//your search form
</form>
by using get instead of post u dont have to use any session variables and u can easily access them in your controller as
$this->input->get('feild_name');
moreover u wont have problem with second page or any other page in pagination as all the search feilds get attached in the url and u can access it easily using $this->input->get('feild_name'); untill your url is changes to something else.. i will aslo save u al ot of coding ..

How to route a query string in a codeigniter Search?

I have a question with route a query string.
I'm trying to route with codeigniter a search. I have some parameters for each search, first a input text field, for a title or a description. Second, I have categories like, Utilities, Games, Exercise... Third, a platform, like, IOS, WP, Android.. and also the page.
I would like to see the route like:
/page/3, or
/page/3/search/whatever or
/cat/Games or
/cat/Games/search/battelfield or
/page/1/cat/games/search/battelfield or
/search/whatever or something like that.
I am using this code for the function in the model:
enter code here
function getAppBy($categ, $page, $str, $platform) {
$perpage = 16;
$offset = ($page-1)*$perpage;
$this->db->select('app.id');
$this->db->from('t_yp_app_category cat, t_yp_user_apps app');
if ($categ != "") {
$this->db->where("cat.name =", $categ);
} if ($str != "") {
$this->db->like('app.yp_title', '%' . $str . '%');
$this->db->like('app.yp_description', '%' . $str . '%');
}if ($platform != "") {
$this->db->like('app.yp_platforms', '%' . $platform . '%');
}
$this->db->limit($perpage,$offset);
$query = $this->db->get();
$result = $query->result_array();
//FIN SELECT
if (sizeof($result) > 0) {
return $result;
} else {
return false;
}
}
enter code here
Thank you. If I havent explained well, let me know
//www.mysite.com/search/sometitle/puzzle/android/4
$route['search/(:any)/(:any)/(:any)'] = 'search/getAppby/$1/$2/$3'; //default without offset, which is why offset defaults to 0
$route['search/(:any)/(:any)/(:any)/(:num)'] = 'search/getAppby/$1/$2/$3/$4'; //default with offset
//You may want to use some regex rather than wildcards ie:(:any)
public function getAppBy($input, $category, $platform, $offset=0){}

Propel to Doctrine Code Snippets

This is a totally newbie question, so please bear with me. I am learning symfony from the online Jobeet and Askeet tutorials, but most of my hacks have involved Doctrine, so I am not familiar at all with Propel. I have managed so far by researching online and modifying to fit my needs, but I need a little help here.
Could someone give me a hand in translating these code snippets into Doctrine?
public function setTag($v)
{
parent::setTag($v);
$this->setNormalizedTag(Tag::normalize($v));
}
public function getTags()
{
$c = new Criteria();
$c->clearSelectColumns();
$c->addSelectColumn(QuestionTagPeer::NORMALIZED_TAG);
$c->add(QuestionTagPeer::QUESTION_ID, $this->getId());
$c->setDistinct();
$c->addAscendingOrderByColumn(QuestionTagPeer::NORMALIZED_TAG);
$tags = array();
$rs = QuestionTagPeer::doSelectRS($c);
while ($rs->next())
{
$tags[] = $rs->getString(1);
}
return $tags;
}
public function getPopularTags($max = 5)
{
$tags = array();
$con = Propel::getConnection();
$query = '
SELECT %s AS tag, COUNT(%s) AS count
FROM %s
WHERE %s = ?
GROUP BY %s
ORDER BY count DESC
';
$query = sprintf($query,
QuestionTagPeer::NORMALIZED_TAG,
QuestionTagPeer::NORMALIZED_TAG,
QuestionTagPeer::TABLE_NAME,
QuestionTagPeer::QUESTION_ID,
QuestionTagPeer::NORMALIZED_TAG
);
$stmt = $con->prepareStatement($query);
$stmt->setInt(1, $this->getId());
$stmt->setLimit($max);
$rs = $stmt->executeQuery();
while ($rs->next())
{
$tags[$rs->getString('tag')] = $rs->getInt('count');
}
return $tags;
}
public static function getTagsForUserLike($user_id, $tag, $max = 10)
{
$tags = array();
$con = Propel::getConnection();
$query = '
SELECT DISTINCT %s AS tag
FROM %s
WHERE %s = ? AND %s LIKE ?
ORDER BY %s
';
$query = sprintf($query,
QuestionTagPeer::TAG,
QuestionTagPeer::TABLE_NAME,
QuestionTagPeer::USER_ID,
QuestionTagPeer::TAG,
QuestionTagPeer::TAG
);
$stmt = $con->prepareStatement($query);
$stmt->setInt(1, $user_id);
$stmt->setString(2, $tag.'%');
$stmt->setLimit($max);
$rs = $stmt->executeQuery();
while ($rs->next())
{
$tags[] = $rs->getString('tag');
}
return $tags;
}
I recommend you to forget Askeet tutorial, it is for the 1.0 release, and lots of things change since this version.
But, you can find a svn dump of a doctrine version of Askeet. You should use SVN to rebuild the repo (don't know how to perform that).
On an other hand, if you need to handle tag in sf1.4 project, I recommend you to use the plugin sfDoctrineActAsTaggablePlugin.

Resources