For a survey I am trying to grab google search result from my php page. I grab six results then i click next button to get next page records.but after 9 pages e.g. 64 results it gives following error:
stdClass Object
(
[responseData] =>
[responseDetails] => out of range start
[responseStatus] => 400
)
I just want as much data as possible. i dont mind if it is google search engine or any other search engine. but for accurate result of survey I want large number of resultset. Can anybody knows how can i do that ?
Is it possible to grab results through cron ? Is there any other way ?
ATTENTION
Google tries to prevent scraping and so servers will be blocked and requests will be dropped when they suspect scraping. So you can use this if you occassionally need to get some google results.
Check google-scraper.squabbel.com for a proxy based scraper and more information on googles blocking mechanism. Its also against their policy and thus illigal.
The google api will not allow for more then 64 results, so if you need more you need to scrape the site yourself. As it was a fun project, I have created a class to do this for you.
It requires the free PHP Simple HTML DOM Parser so you need download this code aswell.
it will output an array like
array(100) {
[0]=>
array(3) {
["title"]=>
string(67) "Online Tests - Online aptitude tests for interview, competitive ..."
["href"]=>
string(36) "http://www.indiabix.com/online-test/"
["description"]=>
string(168) "Online aptitude tests for competitive examination, entrance examination and
campus interview. Take various tests and find out how much you score before
you ... "
}
[1]=>
array(3) {
["title"]=>
string(37) "Test your English - Cambridge English"
["href"]=>
string(50) "http://www.cambridgeenglish.org/test-your-english/"
["description"]=>
string(179) "Test Your English. This is a quick, free online test. It will tell you which Cambridge
English exam may be best for you. Click 'Begin Test' and answer each of the ... "
}
//removed for better visibility
}
How to use:
//start the scraper for google.com (english results)
$gs = new GoogleScraper();
//start the scraper for google.nl (dutch results)
//$gs = new GoogleScraper('https://www.google.nl');
//set your search query
$gs->SearchQuery('online exams');
//start loading the pages. You can enter any integer above 0
$gs->LoadPages(10);
//dump the results, but its just an array so you can also do other things with it.
echo '<pre>';
var_dump($gs->GetResults());
echo '</pre>';
?>
And then the GoogleScraper.php
<?php
require_once('simple_html_dom.php');
class GoogleScraper
{
private $_results;
private $_baseUrl;
private $_searchQuery;
private $_resultsPerPage;
/**
* constructor
* I use the constructor to set all the defaults to keep it all in one place
*/
final public function __construct($baseUrl='')
{
$this->_results = array();
$this->_resultsPerPage = 100;
if (empty($baseUrl)) {
$this->_baseUrl = 'https://www.google.com';
} else {
$this->_baseUrl = $baseUrl;
}
}
/**
* cleanup
*/
final public function __destruct()
{
unset($this->_results);
unset($this->_baseUrl);
unset($this->_searchQuery);
}
/**
* Set the query
*/
final public function SearchQuery($searchQuery)
{
if (!(is_string($searchQuery) || is_numeric($searchQuery)))
{
throw new Exception('Invalid query type');
}
$this->_searchQuery = $searchQuery;
}
/**
* Set the number of results per page
*/
final public function ResultsPerPage($resultsPerPage)
{
if (!is_int($resultsPerPage) || $resultsPerPage<10 || $resultsPerPage>100)
{
throw new Exception('Results per page must be value between 10 and 100');
}
$this->_resultsPerPage = $resultsPerPage;
}
/**
* Get the result
*/
final public function GetResults()
{
return $this->_results;
}
/**
* Scrape the search results
*/
final public function LoadPages($pages=1)
{
if (!is_int($pages) || $pages<1)
{
throw new Exception('Invalid number of pages');
}
if (empty($this->_searchQuery))
{
throw new Exception('Missing search query');
}
$url = $this->_baseUrl . '/search?num='.$this->_resultsPerPage.'&q=' . urlencode($this->_searchQuery);
$currentPage = 1;
while($pages--) {
if ($content = $this->LoadUrl($url)) {
/*
Load content in to simple html dom
*/
$html = new simple_html_dom();
$html->load($content);
/*
Find and handle search results
*/
$items = $html->find('div#ires li');
foreach($items as $item) {
/*
Only normal search results have this container. Special results like found images or news dont have it.
*/
$check = $item->find('div.s');
if (count($check)!=1) {
continue;
}
$head = $item->find('h3.r a', 0);
$result['title'] = $head->plaintext;
/*
If we dont have a title, there is no point in continuing
*/
if (empty($result['title'])) {
continue;
}
$result['href'] = $head->href;
/*
Check if we can parse the URL for the actual url
*/
if (!empty($result['href'])) {
$qs = explode('?', $result['href']);
if (!empty($qs[1])) {
parse_str($qs[1], $querystring);
if (!empty($querystring['q'])) {
$result['href'] = $querystring['q'];
}
}
}
/*
Try to find the description
*/
$info = $item->find('span.st', 0);
$result['description'] = $info->plaintext;
/*
Add the results to the total
*/
$this->_results[] = $result;
}
/*
Find next page
*/
$url = $this->_baseUrl . '/search?num='.$this->_resultsPerPage.'&q=' . urlencode($this->_searchQuery) . '$start=' . ($currentPage*$this->_resultsPerPage);
} else {
throw new Exception('Failed to load page');
}
$currentPage++;
}
}
/**
* Load the url
*/
final private function LoadUrl($url)
{
if (!is_string($url))
{
throw new Exception('Invalid url');
}
$options['http'] = array(
'user_agent' => "GoogleScraper",
'timeout' => 0.5
);
$context = stream_context_create($options);
$content = file_get_contents($url, null, $context);
if (!empty($http_response_header))
{
return (substr_count($http_response_header[0], ' 200 OK')>0) ? $content : false;
}
return false;
}
}
?>
Check this PHP Fiddle to see it in action. Because this could be used quite often from this server, there is a chance for 503 errors from google.
You should add a sleep(1) as cooldown between calls, otherwise might get bebanned.
Did you consider going the official way and getting a google API key?
Related
I am using Peepso plugin for my Wordpress website. Peepso is using Long polling for the part of the chats in order to create real-time chat. I don't know much on Long polling so I am confused in the way it is written inside the add-on file. What I want is to connect this website with my android application so I want to get those data into the android application in real-time. Here is the code of Peepso messages:
/**
* Creates a new message thread and adds the first message.
* #param PeepSoAjaxResponse $resp
*/
public function new_message(PeepSoAjaxResponse $resp)
{
$subject = '';
$message = htmlspecialchars($this->_input->raw('message'));
// SQL safe, forced to int
$participants = array_map('intval',$this->_input->value('recipients', array(), FALSE));
$creator_id = get_current_user_id();
$model = new PeepSoMessagesModel();
$msg_id = $model->create_new_conversation($creator_id, $message, $subject, $participants);
if (is_wp_error($msg_id)) {
$resp->success(FALSE);
$resp->error($msg_id->get_error_message());
$resp->set('subject', $subject);
$resp->set('creator_id', $creator_id);
} else {
$ps_messages = PeepSoMessages::get_instance();
do_action('peepso_messages_new_message', $msg_id);
$resp->success(TRUE);
$resp->notice(__('Message sent.', 'msgso'));
$resp->set('msg_id', $msg_id);
$resp->set('url', $ps_messages->get_message_url($msg_id));
}
}
/**
* Add a message to an existing conversation.
* #param PeepSoAjaxResponse $resp The response object to be sent.
*/
public function add_message(PeepSoAjaxResponse $resp)
{
$message = htmlspecialchars($this->_input->raw('content'));
$parent_id = $this->_input->int('parent_id');
$user_id = get_current_user_id();
// Make sure the "I am typing" flag is deleted
$mayfly = 'msgso_typing_' . $user_id. '_' . $parent_id;
PeepSo3_Mayfly::del($mayfly);
// And that another one can't be set for another full second
$mayfly_just_posted = "msgso_posted__{$user_id}_{$parent_id}";
PeepSo3_Mayfly::set($mayfly_just_posted,1,1);
$participants = new PeepSoMessageParticipants();
if (FALSE === $participants->in_conversation($user_id, $parent_id)) {
$resp->success(FALSE);
$resp->error(__('You are not part of this conversation.', 'msgso'));
} else {
$model = new PeepSoMessagesModel();
$msg_id = $model->add_to_conversation($user_id, $parent_id, $message);
if (FALSE === $msg_id) {
$resp->success(FALSE);
$resp->error(__('Failed to send message.', 'msgso'));
} else {
$resp->success(TRUE);
$resp->notice(__('Message sent.', 'msgso'));
$resp->set('msg_id', $msg_id);
}
}
}
And here is the js file that controls the long polling:
/**
* Starts long-polling to get chat stack state.
*/
startLongPolling: function () {
if (!SSE_ENABLED && !this.sessionExpired) {
this.stopLongPolling();
this.fetchChatState().done(
$.proxy(function () {
this.checkChatState();
}, this)
);
}
},
/**
* Stops long-polling to get chat stack state.
*/
stopLongPolling: function () {
if (!SSE_ENABLED) {
clearTimeout(this.checkChatTimer);
this.checkChatXHR && this.checkChatXHR.abort();
this.fetchChatXHR && this.fetchChatXHR.ret && this.fetchChatXHR.ret.abort();
this.checkChatXHR = false;
this.fetchChatXHR = false;
}
}
I tried to get those data by adding this line into the code echo json_encode($resp, JSON_PRETTY_PRINT); but nothing happened. So, how can I get the data from this code above? If you need any further information please let me know as I am new on long polling and how to connect it with android application. Any answer will be appreciated.
I'm using AlexaCRM Toolkit for Dynamics 365 and I'm working on script that bring me results of all invoices for specific contact, the following script show the last ten invoices but for different contacts.
$invoices = $service->retrieveMultipleEntities("invoice", $allPages = false, $pagingCookie = null, $limitCount = 10, $pageNumber = 1, $simpleMode = false);
foreach ($invoices->Entities as $invoice) {
echo 'ID : ' . $invoice->ID . '<br>';
echo 'Name :' . $invoice->name. '<br>';
}
The objective is to get only invoices related to specific contact.
Dear #Arun thank you for interaction , i used retrieveMultiple and i construct a FetchXML query to get all relative invoices this is the code
$queryXML = "<fetch mapping='logical'>
<entity name='invoice'>
<all-attributes />
<link-entity name='contact' from='contactid' to='customerid' alias='C'>
<filter type='and'>
<condition attribute='contactid' operator='eq' value='$contact->id' />
</filter>
</link-entity>
</entity>
</fetch>";
$invoices = $service->retrieveMultiple($queryXML, false);
Contact (lookup) is a Foreign key GUID in Invoice entity record. Without filtering the Contact value you are looking for, the query will result you random 10 Invoices from top.
Try to set the WHERE condition in your code.
For example:
To filter the Contact by its emailaddress1 attribute, the sample in github look like this:
$contactKey = new \AlexaCRM\CRMToolkit\KeyAttributes();
$contactKey->add( 'emailaddress1', $contactKeyValue );
Another example referring this, you can do something like below to filter the particular Parent Contact.
/* Check the ID or AlexaCRM\CRMToolkit\KeyAttributes to retrieve the entity values */
if ($IDorKeyAttributes != null) {
/* Set EntityValues if specified Entity ID */
if (is_string($IDorKeyAttributes) && self::isGuid($IDorKeyAttributes)) {
/* Set the ID of Entity record */
$this->setID($IDorKeyAttributes);
/* Get the raw XML data */
$rawSoapResponse = $this->client->retrieveRaw($this, $columnSet);
/* NOTE: ParseRetrieveResponse method of AlexaCRM\CRMToolkit\AlexaSDK_Entity class, not the AlexaCRM\CRMToolkit\AlexaSDK class */
$this->parseRetrieveResponse($this->client, $this->LogicalName, $rawSoapResponse);
} else {
if ($IDorKeyAttributes instanceof KeyAttributes) {
if (version_compare($this->client->organizationVersion, "7.1.0", "<")) {
throw new Exception('Entity ID must be a valid GUID for the organization version lower then 7.1.0');
}
/* Set the keyAttributes array */
$this->keyAttributes = $IDorKeyAttributes;
/* Add the KeyAttribute values to the entity object values */
foreach ($IDorKeyAttributes->getKeys() as $key => $attribute) {
$this->propertyValues[$key] = array("Value" => $attribute, "Changed" => true);
}
/* Get the raw XML data */
try {
$rawSoapResponse = $this->client->retrieveRaw($this, $columnSet);
/* NOTE: ParseRetrieveResponse method of AlexaCRM\CRMToolkit\AlexaSDK_Entity class, not the AlexaCRM\CRMToolkit\AlexaSDK class */
$this->parseExecuteRetrieveResponse($this->client, $this->LogicalName, $rawSoapResponse);
} catch (SoapFault $sf) {
$errorCode = $sf->faultcode;
// undocumented feature
if ($errorCode == '-2147088239') {
$this->exists = false;
}
/* ToDo: Return exception with user-friendly details, maybe KeyAttribute parameters invalid */
}
}
}
}
Note: Am not a php person, to help you I did some research.
All,
Background of how problem was detected
My question concerns the performance of a web app, mainly the index page. I noticed the problem when I was giving a demonstration at a local branch of my company that has slow internet (I don't know the exact speeds, or ping rate) judged by the fact that Google took about 10 seconds to load. My index page took ~10-20 times longer to load. I was under the assumption that my app did most of the work on the server side (as php is making all of the database queries...). But this led me to look at the network tool of Chrome and see the latency times of these 4 divs being loaded by ajax (I'll elaborate in a bit). Interestingly, the scripts being called appear to run sequentially, but not necessarily in the order I invoked the ajax calls (sometimes they do, other times they don't).
What are these divs / ajax requests?
Here is a code snippets of a request:
Yii::app()->clientScript->registerScript('leftDiv', '
$( "#left_dash" ).load(
"'.$this->createUrl("/site/page?view=leftDashLoad") .'",
function(){
$("#left_dash p a").click(function() {
$(this).parent().parent().find("div.scroll100").slideUp();
$(this).parent().next().stop(false, false).slideDown();
});
$("p:first-child").next().slideDown();
}
);
' );
Here is the page requested:
$this->widget('widgets.ScrollList',array(
'condition'=>
function($muddJob,$scrollList)
{
$job = $muddJob->Job;; //returns a job or empty array
if(!empty($job) )
{
if( $muddJob->uploadArtwork == null && $muddJob->uploadData == null ) {
array_push($scrollList->_models,$job);
$scrollList->columnValues = array($muddJob->jobDescription,$muddJob->dropDate1);
return true;
}
}
return false;
},
'columns' => array('col1'=>"MuddJob#",'col2'=>"Desc",'col3'=>"Dealer Name"),
'name'=> "Print New Ticket",
'muddJobs' => $currentExchanges->getCurrentMuddExchanges(),
)
);
Imagine that page (the page that ajax has called) having 6 similar declarations that create widgets. The goal is to return html to put back in place of a loading gif on the index page.
Here is the scroll widget:
<?php
Yii::import('widgets.ScrollListBase');
include_once Yii::app()->extensionPath . "/BusinessDay.php";
class ScrollList extends ScrollListBase
{
private $_content;
public $columns = array();
public $columnValues;
private $_listInfo;
public $name;
public $_models = array();
public $condition;
public $muddJobs; //object to pass
public $jobsMailingTodayArray = array();
public function init()
{
//$this->init();
$this->_listInfo = $this->generateListInfo($this->columns);
//$muddJobs = $this->getCurrentMuddExchanges();
$listInfo = $this->newScrollList($this->muddJobs);
$contents = $this->createContent($listInfo,$this->name);
$this->_content = $contents[0];
// $this->_fullTableContent = $contents[1];
//$this->_listInfo = $contents[2];
}
public function run()
{
//if($this->data['isVisible'])
echo $this->_content;
Yii::app()->session["exploded_content_{$this->name}"] = $this->_models;
}
private function newScrollList($muddJobs)
{
$listInfo = $this->_listInfo;
$tempCount = 0;
foreach($muddJobs as $muddJob)
{
$condition = $this->condition;
if($condition($muddJob,$this) && empty($this->jobsMailingTodayArray) ) //if no job exists for the muddExchange...
{
$tempArray = $this->createWidgetLinks($tempCount,$listInfo,$muddJob,$this->columnValues);
$listInfo = $tempArray[0];
$tempCount = $tempArray[1];
}
elseif ( !empty($this->jobsMailingTodayArray ) )
{
foreach ($this->jobsMailingTodayArray as $jobMailingToday) //change to for loop over the length of the jobsMailingToday
{
$tempArray = $this->createWidgetLinks($tempCount,$listInfo,$muddJob,$this->columnValues);
$listInfo = $tempArray[0];
$tempCount = $tempArray[1];
}
$this->jobsMailingTodayArray = array();
}
}
return array($listInfo,$tempCount);
}
}
?>
Here is it's parent:
<?php
class ScrollListBase extends CWidget
{
private $content = "<p>";
private $divDeclaration = "<div class='scroll100'>\n<table class='quickInfoTable'>\n<thead>\n";
private $headTag = "<th>";
private $headTagClose = "</th>\n";
private $theadTagClose = "</thead>\n";
private $bodyTag = "<tbody>\n";
private $listInfo = "<div class='scroll100'>\n<table class='quickInfoTable'>\n<thead>\n<th>Job#</th>\n<th>Package#</th>\n<th>Entry Date</th>\n</thead>\n<tbody>\n";
/**
* Initializes the widget.
*/
public function createContent($listInfo,$name)
{
$largeHref = Yii::app()->request->baseUrl . '/index.php/site/fullTableView';
$this->content .= "<span class='badge' >{$listInfo[1]} </span> <a href='#'>{$name} </a> <a href='$largeHref/Name/{$name}'> <small>(view larger)</small> </a> </p>";
if( $listInfo[1] > 0 )
{
// $this->fullTable .= substr($listInfo[0],22);
// $this->fullTableContent= $this->fullContent .= $this->fullTable . "</tbody>\n</table>\n</div>";
$this->content .= $listInfo[0] . "</tbody>\n</table>\n</div>";
}
return array($this->content);
}
//Helper Methods
/**
*
* #param type $attributeArray. send an accociative array
* #return type = either a job or an empty array
*/
protected function getJobByAttributes($attributeArray)
{
return Jobs::model()->with('MuddExchange')->findByAttributes($attributeArray);
}
protected function createWidgetLinks($tempCount,$listInfo,$muddJob,$columnValues,$url="/MuddExchange/")
{
$tempCount++;
$viewIndex = $muddJob->exchange_id;
$model = $muddJob;
$job = $muddJob->Job;
if ( isset($job ))
{
$model = $job;
$url = "/Jobs/";
$viewIndex = $model->job_id;
}
$link = CHtml::link("$model->jobNumber",array("{$url}{$viewIndex}"));
$listInfo .= "<tr>\n<td>$link</td>\n";
foreach ($columnValues as $columnValue)
{
$listInfo .= "<td>{$columnValue}</td>\n";
}
$listInfo .= "</tr>";
return array($listInfo,$tempCount);
}
protected function getListInfo()
{
return $this->listInfo;
}
/**
* Takes an array of strings to generate the column names for a particular list.
* #param array $heads
* #return string
*
*/
protected function generateListInfo($heads)
{
//<th>Job#</th>\n<th>Package#</th>\n<th>Entry Date</th>\n</thead>\n<tbody>\n";
$htmlScrollStart = $this->divDeclaration;
foreach ($heads as $tableColumn => $name)
{
$htmlScrollStart .= $this->headTag . $name . $this->headTagClose;
}
$htmlScrollStart .= $this->theadTagClose . $this->bodyTag;
return $htmlScrollStart;
}
public function calculateDueDate($jobsMailDate,$job)
{
$package = PackageSchedule::model()->findByAttributes(array('package_id'=>$job->packageID));
$projectedDays = $package->projected_days_before_mail_date;
$dropDate1 = $jobsMailDate->projected_mail_date;
$dropDate = wrapBusinessDay($dropDate1); //use this for actual command...
$toSec = 24*60*60;
$dayInt =0;
$secDropDate = strtotime($dropDate1);
do{
$dayInt +=1;
$daysInSec = ($dayInt) * $toSec ;
$secGuessDueDate = $secDropDate - $daysInSec;
$dueDate = date('Y-m-d',$secGuessDueDate);
$difference = $dropDate->difference($dueDate);
}while( $difference != $projectedDays);
return $dueDate;
}
}
?>
Why I think this behavior is odd
The whole slow internet thing is a beast in and of itself, but I don't think that is in the scope of StackOverflow. I'm more concerned about the loading of these divs. The div that loads last, i.e., takes on average 1.5 to 2 seconds, is an ajax request to a page that creates a single widget. The logic behind it is here:
<?php
include_once Yii::app()->extensionPath . "/CurrentExchanges.php";
$currentExchanges = Yii::app()->session['currentExchanges'];
$this->layout = 'barebones';
$this->widget('widgets.ScrollList',array(
'condition'=>
function($muddJob,$scrollList)
{
if ($muddJob->dropDate1 != null && $muddJob->dropDate1 != '0000-00-00')
{
$job = $muddJob->Job;;
if(!empty($job) && $job->packageID != null) //if job exists for the muddExchange and has a package
{
if($job->uploadArtwork == null )
{
$jobsMailDate = JobsMailDate::model()->findByAttributes(array("job_id"=>$job->job_id,'sequence_num'=>1));
if(!empty($jobsMailDate))
{
$calculatedDueDate = $scrollList->calculateDueDate($jobsMailDate,$job);
if (strtotime($calculatedDueDate) <= strtotime(date("Y-m-d")) )
{
array_push($scrollList->_models , $job);
$scrollList->columnValues = array($muddJob->jobDescription,$muddJob->dropDate1,$jobsMailDate->projected_mail_date);
return true;
}
}
}
}
}
return false;
},
'columns' => array('col1'=>"MuddJob#",'col2'=>"Desc",'col3'=>"Drop Date", 'col4' =>'Projected Drop Date'),
'name'=> "Artwork Due Today",
'muddJobs' => $currentExchanges->getCurrentMuddExchanges(),
)
);
?>
The calculateduedate method makes 2 additional calls to the server.
What I'm failing to understand is why the left div (with the most proccessing to do) is usually the first to return and the artLoad is usually the last to load (by a substantial difference). Here are some times returned by chromes network tool:
leftDashLoad: 475ms
rightDashLoad: 593ms
dataLoad: 825ms
artLoad: 1.41s
dataLoad: 453ms
rightDashLoad: 660ms
leftDashLoad: 919ms
artLoad: 1.51s
rightDashLoad: 559ms
leftDashLoad: 1.17s
dataLoad: 1.65s
artLoad: 2.01s
I just can't fathom why the left/right dashloads return so much faster than the artLoad. The code for artLoad and dataLoad are nearly identical save the actual comparison (the one if statement). If this were truly asynchronous, I'd expect the order to be art/dataLoad, rightDashLoad and leftDashLoad based purely on the amounts of computation done in each page. Perhaps the server isn't multithreading, or there is some weird configuration, but if that were the case, I don't see why the effects of the loading would be hit so hard by slow internet.
If I have overlooked something obvious or failed to use google appropriately, I do apologize. Thanks for any help you can offer!
Language/other tech info
The app was developed using the Yii framework. PHP 5.3. Apache Server. INNODB Tables. Server is hosted in dreamhost.
Update
I've changed the view page so that the ajax calls are now calling a controller action. It seems to have made the loading times more similar (asynchronous?) on my local dev environment, but really slowed it down on the QA environment (hosted on dreamhost...). Here is screen shot of the local network tools info:
dev environment
and the qa site (note, that the databases have about the same amounts of data...)
qa environment
Thoughts? It seems to me that my original problem may be solved, as the return times (on my local dev) look more like I expect them to.
Also, my own ignorance as to how NetBeans debugs was playing a part in this synchronous loading thing as xdebug is using a session. I believe this was forcing the ajax calls to wait their turn.
Thanks to #Rowan for helping me diagnose this strange behavior. PHP was trying to request a session before the session was closed in hopes to prevent a race hazard. There were session requests in my code, and there was a session started by my IDE (NetBeans). Removing all references to sessions in the ajax called pages and having ajax call actions that use renderPartial() proved to return the expected behavior (and much better code IMO). Let me know how to properly thank users in terms of StackOverflow (can I up comments, or what is there available? ). Thank you all!
I am trying to build a page in Magento that essentially lists all products in a specific category WITH layered navigation (filters on the left side in my case), but the list must be randomly generated once a day.
I have tried to use "product_list_random", but it seems to ignore CategoryIDs settings.
Investigating further, I located Random.php and the problem is that protected function _getProductCollection in Random.php simply starts from a full product collection, whereas in List.php, it has some logic to fetch the current category.
Now I am battling to adapt the code from List.php to Random.php, but no success...
Here is my code so far:
$layer = $this->getLayer();
/* #var $layer Mage_Catalog_Model_Layer */
$origCategory = null;
if ($this->getCategoryId()) {
$category = Mage::getModel('catalog/category')->load($this->getCategoryId());
if ($category->getId()) {
$origCategory = $layer->getCurrentCategory();
$layer->setCurrentCategory($category);
}
}
//$this->_productCollection = $layer->getProductCollection();
$this->_productCollection = $layer->getSelect()->order('rand()');
$this->prepareSortableFieldsByCategory($layer->getCurrentCategory());
if ($origCategory) {
$layer->setCurrentCategory($origCategory);
}
Now it seems that you can't "rand" Magento's layer model, but I am a bit lost here. Any ideas as how to finish this? I can sense I am very close, but am missing something... :(
Thanks in advance!
UPDATE 2
Ok. I think I got what's happening. I rewrote the code in a "more optimized/rational" way and still get the paging problem.
I xdebugged part of the rendering process and what seems to be happening is that Toolbar.phtml/php is called PRIOR TO Random.php. The reason for that is two-fold: (1) My Layout actually states toolbar as a child to Random, so it should be called first; and (2) It HAS TO be called first, as the Toolbar needs to know how many elements it will be paging.
Because of this, once I call setCollection in my code, all the paging parameters are already there, so paging breaks... :( That's my theory.
The only workaround I can think of is to reset all toolbar parameters after my collection loads, which (1) seems irrational (there's got to be a better way!) and (2) I'm not sure how to pull it out... :( :(
The current code follows... As usual, any pointers would be much appreciated!
protected function _getProductCollection()
{
//xdebug_start_trace('/Library/WebServer/talcha/var/log/random-trace.txt');
/* get ready to build collection */
$collection=Mage::getResourceModel('catalog/product_collection');
$genlayer=Mage::getModel('catalog/layer');
$genlayer->prepareProductCollection($collection);
$collection->addAttributeToFilter('status',1); //only enabled product
$collection->addAttributeToFilter('visibility',4); //only 'catalog,search' products
$collection->addStoreFilter();
/* Find out where I am */
$newrandom=false; // flag to check if random collection was generated
$interna=false;
$currentUrl = $this->helper('core/url')->getCurrentUrl();
Mage::log("URL:".$currentUrl,null,"paulo.log");
if (strpos($currentUrl,"chas")!==false) { // estou em CHAS
$interna=true;
}
/* if I am where I need to be, then check whether I need to update the collection */
if ($interna) {
$now=time();
/* fetch collection file name and generated time */
if (strpos($currentUrl,"?")===false) {
$name=substr($currentUrl,strpos($currentUrl,"index.php")+10);
} else {
$name=substr($currentUrl,strpos($currentUrl,"index.php")+10,strpos($currentUrl,"?")-strlen($currentUrl));
}
if ($name=="chas2") { $name="chas"; }
Mage::log("Nome:".$name,null,"paulo.log");
$dir=Mage::getBaseDir('tmp');
$dh=opendir($dir);
while (($file=readdir($dh))!=false) {
Mage::log("Arq.:".$file,null,"paulo.log");
if (strpos($file,$name)!==false) {
$epoch=substr($file,strpos($file,"-")+1);
$fname=$file;
Mage::log("Hora:".$epoch,null,"paulo.log");
break;
}
}
/* tests whether elapsed time between now and last update is longer that "x" seconds */
/* if true, generates new random collection, serialize it and flag it */
/* if false, just picks up last generated collection from file */
if (($now-$epoch) > 3600) { //set 1 hour
$collection->addCategoryFilter(Mage::getModel('catalog/category')->load($this->get_cur_category()));
$collection->getSelect()->order('rand()');
file_put_contents($dir."/".$name."-".$now,serialize($collection));
unlink($dir."/".$fname);
$newrandom=true;
Mage::log("Fiz shuffle!",null,"paulo.log");
Mage::log("Count:".$collection->count(),null,"paulo.log");
} else {
$collection=unserialize(file_get_contents($dir."/".$fname));
}
} // close if($interna)
/* build the final collection. If we are using the generated collection, we need
to adjust pagination according to request parameters.
If we are filtering, then we set proper filters and load the ordinary collection
(no random stuff when filtering through) */
if (is_null($this->_productCollection)) {
$layer=$this->getLayer();
foreach($this->getRequest()->getParams() as $key=>$value) {
Mage::log("Random:".$key."=>".$value,null,"paulo.log");
switch ($key) {
case "p":
$collection->setPageSize($this->get_prod_count())->setCurPage($value);
break;
case "cat":
$collection->addCategoryFilter(Mage::getModel('catalog/category')->load($value));
break;
case "price":
list($range,$step)=explode(",",$value);
$gt_value=($range-1)*$step;
$lt_value=$range*$step;
$collection->addAttributetoFilter($key,array('gt'=>$gt_value));
$collection->addAttributetoFilter($key,array('lteq'=>$lt_value));
break;
case "limit":
$collection->setPageSize($value)->setCurPage(1);
break;
case "page_id":
case "dir":
case "order":
case "mode":
break;
default:
$collection->addAttributeToFilter($key,$value); //filters
}
}
/* if we are filtering, then the collection wasn't loaded yet */
if (!$interna) {
$collection->load();
}
}
$this->setCollection($collection);
$this->_productCollection=$collection;
return $this->_productCollection;
}
<?php // get Random prdocut list
$_productCollection = Mage::getResourceModel('catalog/product_collection');
Mage::getModel('catalog/layer')->prepareProductCollection($_productCollection);
$_productCollection->getSelect()->order('rand()');
$_productCollection->addStoreFilter();
$numProducts = $this->getNumProducts() ? $this->getNumProducts() : 20;
$_productCollection->setPage(1, $numProducts);
if (sizeof($_productCollection) < $numProducts) {
$numProducts = sizeof($_productCollection);
}
$displayed_products = array();
foreach ($_productCollection as $_product) {
$displayed_products[] = $_product;
}
$random_products = array();
if (sizeof($_productCollection) > 1) {
$random_products = array_rand($displayed_products, $numProducts);
} else {
$random_products = array('0');
}
?>
I need detect user's country and show website's language by him / her country . (Turkish for Turkish people, English for all others)
How can i do this fastest way ? Performance is important for me .
I'm looking IPInfoDB' API , are there any better alternative ?
(I'm using PHP)
Well for people who might visit in 2017 this is a solution this extremely simple to use
<button class="btn dropdown-toggle" style="cursor:pointer;z-index:1000!important;margin-top:-67px;background:none!important;font-size:1.4em;" onclick="window.location.href='language'">
(a) <?php
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];}
elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
$url = "http://api.wipmania.com/".$ip;
(h) $country = file_get_contents($url); //ISO code for Nigeria it is NG check your country ISO code
?>
<?php if ($country == ""){
echo "Country not found";
} else { ?>
<script>
var map = "<?php echo $country ?>";
$.ajax({
type : 'POST',
url : 'http://www.mowemy.com/countryflag.php',
data : {map: map},
success : function(r) {
//console.log("success: " + r);
$('#mumil').html(r);
} })
</script>
<?php } ?>
<div id ="mumil" style="font-size:13px!important;margin-top:5px;"></div>
</button>
let me beak it down letter A - H is the script to detect your ISO number for the country for my country Nigeria it is NG you can google search your countries ISO number, with this script it is auto detected. Then i created a page with some info you fire AJAX to that page which it sends back the country flag color and name simple and easy PLEASE CALL JQUERY BEFORE AJAX TO RUN THE AJAX UNLESS IT WONT WORK GOODLUCK
You could use the API here http://www.hostip.info/use.html if you're okay with relying on an external site.
You can also use the GeoIP PHP API
Of course, implementing the script Orbit linked might just save you the hassle of going through the API's.
Good luck.
The best way to do this I have found is using the "GAE-IP-TO-COUNTRY" library:
https://github.com/andris9/GAE-IP-TO-COUNTRY/tree/master/src/ip_files
Example of use (you have to copy the "ip_files" directory to your server):
function iptocountry($ip) {
$numbers = preg_split( "/\./", $ip);
include("./ip_files/".$numbers[0].".php");
$code=($numbers[0] * 16777216) + ($numbers[1] * 65536) + ($numbers[2] * 256) + ($numbers[3]);
foreach($ranges as $key => $value){
if($key<=$code){
if($ranges[$key][0]>=$code){$country=$ranges[$key][1];break;}
}
}
return $country;
}
$country=iptocountry($_SERVER['REMOTE_ADDR']);
echo $country;
As others have pointed out, it would probably a better idea to check the Accept-Language HTTP Header for Turkish. If it's the preferred language, serve it. Otherwise serve English.
Here's some code.
I coded the next stuff using Accept-Language as other users pointed:
function GetAcceptedLangs()
{
$res=array();
$a=getallheaders();
if(isset($a["Accept-Language"]))
{
$aceptlangs=explode(",",str_replace(array(';','0','1','2','3','4','5','6','7','8','9','.',"q="),array(',','','','','','','','','','','','',""),$a["Accept-Language"]));
foreach($aceptlangs as $i=>$v)
{
if(trim($v)!="")
$res[]=trim($v);
}
}
return $res;
}
A simple
print_r(GetAcceptedLangs());
return in my case:
Array ( [0] => es-ES [1] => es [2] => en )
You can after define an array like this one to change to your internal language value, for example:
$al={"ES-es"=>"es","es"=>"es","en"=>"en"......}
They are already sorted by user preferences.
If all languages don't exists in the array you can go to the default language of your website. This is valid also if the browser don't send the Accept-Language header.
Another version removing the sub-region values
function GetAcceptedLangs2()
{
$res=array();
$a=getallheaders();
if(isset($a["Accept-Language"]))
{
$aceptlangs=explode(",",str_replace(array(';','0','1','2','3','4','5','6','7','8','9','.',"q="),array(',','','','','','','','','','','','',""),$a["Accept-Language"]));
foreach($aceptlangs as $i=>$v)
{
$va=trim($v);
if(($pos=strpos($va,"-"))!==false)
$l=substr($va,0,$pos);
else
$l=$va;
if($l!="" && !isset($check[$l]))
{
$check[$l]=1;
$res[]=$l;
}
}
}
return $res;
}
It would return in my case
Array ( [0] => es [1] => en )
Here is the straight forward way I like to use
<?php
class GeoIp
{
protected $api = 'https://ipapi.co/%s/json/';
protected $properties = [];
//------------------------------------------------------
/**
* __get
*
* #access public
* - #param $key string
* - #return string / bool
*/
public function __get($key)
{
if( isset($this->properties[$key]))
{
return $this->properties[$key];
}
return null;
}
//------------------------------------------------------
/**
* request_geoip
*
* #access public
* - #return void
*/
public function request_geoip($ip)
{
$url = sprintf($this->api, $ip);
$data = $this->send_geoip_request($url);
$this->properties = json_decode($data, true);
//var_dump($this->properties);
}
//------------------------------------------------------
/**
* send_geoip_request
*
* #access public
* - #param $url string
* - #return json
*/
public function send_geoip_request($url)
{
$loc_content = file_get_contents($url);
return $loc_content;
}
}
//You can access it like this:
$geo = new GeoIp;
$geo->request_geoip('foo');
echo $geo->country;
?>
Visit this website for more info