Batch API, inviting friends - events

I'm trying to invite all fans of a page to a facebook event using the batch API. It's the first time I'm using this API, and at this time I have nothing to test this part of code... can someone tell me how to test without using a real page, with real fans... or tell me if it looks correct ?
//Getting all the likers of the page
$result = $facebookObj->api(array(
'method' => 'fql.query',
'query' => 'select uid,name from user where uid in ( select uid from page_fan where uid in (select uid2 from friend where uid1 = me()) and page_id = '.$fbPage.')'
));
//If liker are more than 50 we use batch request
if($numLikers >50){
// split array into several part of 50 users accounts
$splitLikers = array_chunk($result, 50);
// count how many arrays are generated by the array_chunk
$countSplit = count($splitLikers);
//Start a loop through the numbers of groups of 50 users (or less if last group contains less than 50 users
for($a=0; $a<$countSplit; $a++){
//Second loop to go through the 50 likers in one group
for($b=0; $b<count($splitLikers[$a]); $b++){
// construct an array containing the whole group
$queries[$a] = array('method' => 'POST', 'relative_url' => $event_fbID . "/invited/" . $splitLikers[$a][$b]['uid']);
}
//Send POST batch request with the array above
$ret_val = $facebookObj->api('/?batch='.json_encode($queries[$a]), 'POST');
}
}else{
foreach ($result as $value) {
$ret_val = $facebookObj->api($event_fbID . "/invited/" . $value['uid'],'POST');
if($ret_val) {
// Success
$numInvited++;
}
}
}

Your code seems ok, but dont forget you may need to add the access token to the batch request
like:
$params['access_token']=[USER_ACCESS_TOKEN];
$ret_val = $facebookObj->api('/?batch='.json_encode($queries[$a]), 'POST', $params);
And for testing you may want to create a new page with a couple of friends added and test it.
Also remember you need to handle the response, the batch request will response with an array with the same length that the batch array, and each object may be a diferent code, true if it was send.
Also you can try your FQL code in the Graph Explorer (clic here)
Seems ok, but it shows a extrange result.

Related

A huge discrepancy between the number of unique visitors reported in Google Analytics and those tracked by the Custom unique visitor count

I implemented a custom unique visitor counter to my website developed in Codeigniter. The visitor counter is based on cookies as follows
On Page Load
$this->load->helper('cookie');
$check_visitor = $this->input->cookie(urldecode($pageTitle + $slug), FALSE);
$ip = $this->input->ip_address();
if ($check_visitor == false) {
$cookie = array(
"name" => urldecode($pageTitle + $slug),
"value" => "$ip",
"expire" => time() + 1800,
"secure" => false
);
$this->input->set_cookie($cookie);
// then increase count by one in the table
$this->news->update_counter(urldecode($slug));
}
My cookie expiration time is 30 mins.
When I compare my custom counts with Google analytics counts there is a large difference.
I also added a list of crawlers and bots to filter the counts but didn't work.
Can someone help me out to address this problem?

How to append key value to the result of factory()->make() in Laravel?

I am writting a Test Case.
Lets say there is a Threads model with fields: user_id, title, body
So to test wheather submitting the Thread works, I am doing this:
$user = factory('App\User')->create();
$thread = factory('App\Thread')->make(['user_id' => $user->id]);
$this->post('/threads', $thread->toArray());
$this->get('/thread/'.$thread->id)
->assertSee($thread->title)
But I have yet another field that I want to post along with the thread which is not part of the thread model.
eg.
community => 'some_community'
So how do I append another field before posting the array to /threads.
How can I append key value pair to the result of make() ???
You can use the data_set function:
$thread = factory('App\Thread')->make(['user_id' => $user->id]);
$payload = $thread->toArray();
$this->post('/threads', data_set($payload, 'community', 'some_community'));

CodeIgniter session shows different output in different functions

In first function of my controller, I am fetching random records from mysql table using CI active record
$query = $this->db->query("SELECT DISTINCT * FROM questions WHERE `level` = '1' ORDER BY RAND() limit 0,5");
$result = $query->result_array();
and saving result in session as
// saving questions id in session
for($i = 0; $i < count($result); $i++)
{
$session['questionsId'][] = $result[$i]['qId'];
}
$this->session->set_userdata($session);
and if print session variable it shows output like:
$qIds_o = $this->session->userdata('questionsId');
var_debug($qIds_o);
Array
(
[0] => 5
[1] => 9
[2] => 3
[3] => 6
[4] => 11
)
but if I retrieve same session in another function of same controller it shows different result
$qIds = $this->session->userdata('questionsId');
var_debug($qIds);
Array
(
[0] => 2
[1] => 8
[2] => 6
[3] => 3
[4] => 5
)
and if I remove ORDER BY RAND() from mysql query like:
$this->db->query("SELECT DISTINCT * FROM questions WHERE `level` = '1' limit 0,5");
it shows same session array in both functions. Very strange.
Please guide what is going wrong....
Here is my controller script:
public function set_value(){
$query = $this->db->query("SELECT DISTINCT * FROM questions WHERE `level` = '1' ORDER BY RAND() limit 0,5");
$result = $query->result_array();
// saving questions id in session
for($i = 0; $i < count($result); $i++)
{
$session['questionsId'][] = $result[$i]['qId'];
}
$this->session->set_userdata($session);
$qIds_o = $this->session->userdata('questionsId');
var_debug($qIds_o);
}
public function get_value(){
$qIds = $this->session->userdata('questionsId');
var_debug($qIds);
}
I called set_value() on page load while once the page loaded I call get_value() using AJAX post which simply hits my_controller/get_value/ and response back to browser.
Without looking at your controller (let's call it my_controller in detail, I think what might be happening is:
(1) You call the first function, my_controller/set_value where set_value sets the session variable and you echo the result.
(2) You then call the second function, show_value that simply echos out the session variable.
What you might be doing in set_value is:
1) echo out the current session variable
2) call the query and re-set the session variable
If this is the case, then when you go to show_value (2nd function) you are looking at the recently re-set value instead of the prior value that you echoed out in the first function.
I have two questions about this sentence:
but if I retrieve same session in another function of same controller it shows different result
Is this on a new page load?
If so, is the query run again?
I'm going to assume the answer to both of these questions is yes, since you said removing RAND() gives you the same results.
You are using a combination of RAND() and LIMIT in your query, meaning you want only five rows in a random order. That means that each time the query is run (and your session data is set), it is very likely that the results will be different.
I don't know exactly what you're doing with these IDs and what sort of data set you need, so this may not be 100% perfect for your solution, but if you only need to set this session data once, you should check if it exists before running the query.
if ($this->session->userdata('questionsId') === FALSE)
{
// Run your query and set your session data here.
// Note that in CI 3.0, Session::userdata()
// will return NULL if empty, not a boolean.
}

Magento - cms/page collection - apply filter to return only pages which are unique to a given store id (ie not also assigned to other stores)

I can use:
Mage::getModel('cms/page')->getCollection()->addStoreFilter($store_id);
to retrieve a collection of CMS pages filtered by Store Id.
But how do I get it to remove ones which are also assigned to other stores?
ie: I do not want it to return items which have 'All Store Views' as the Store View. (Or any other additional store id assigned to that CMS page.) It has to only return pages unique to that one store.
I am extending the Aitoc permissions module, so that Store Admins cant view or edit CMS pages and static blocks which might impact other stores. This involves filtering those items from the grid.
There's no native collection method to do this, so you'll need to
Query the cms_page_store table for pages unique to a given store
Use the results from above in your filter
I didn't fully test the following, but it should work (and if it doesn't, it'll give you a good start on your own query)
$page = Mage::getModel('cms/page');
$resource = $page->getResource();
$read = $resource->getReadConnection();
#$select = $read->query('SELECT page_id FROM ' . $resource->getTable('cms/page_store') . ' GROUP BY store_id');
//set total count to look for. 1 means the page only appears once.
$total_stores_count_to_look_for = '1';
//get the table name. Need to pass through getTable to ensure any prefix used is added
$table_name = $resource->getTable('cms/page_store');
//aggregate count select from the cmd_page_store database
//greater than 0 ensures the "all stores" pages aren't selected
$select = $read->query('SELECT page_id as total
FROM '.$table_name.'
WHERE store_id > 0
GROUP BY page_id
HAVING count(page_id) = ?',array($total_stores_count_to_look_for));
//fetch all the rows, which will be page ids
$ids = $select->fetchAll();
//query for pages using IDs from above
$pages = Mage::getModel('cms/page')->getCollection()->addFieldToFilter('page_id',array('in'=>$ids));
foreach($pages as $page)
{
var_dump($page->getData());
}
If you have thousands and thousands of CMS pages it may be worth it to alter the cms/page collection's select to join in aggregate table data. I'll leave that as an exercise for the reader, as those sorts of joins can get tricky.
$collection = Mage::getModel('cms/page')->getCollection();
$collection->getSelect()
->join(
array('cps' => $collection->getTable('cms/page_store')),
'cps.page_id = main_table.page_id AND cps.store_id != 0',
array('store_id')
)
->columns(array('stores_count' => new Zend_Db_Expr('COUNT(cps.store_id)')))
->group('main_table.page_id')
->having('stores_count = ?', 1)
->having('cps.store_id = ?', $storeId)
;
Fusing some elements of the solutions proposed by Alan and Vitaly with my own cumbersome understanding, I achieved what I needed with the following code.
To put into context, I was extending the Aitoc permissions module, so that Store Admins cant view or edit CMS pages and static blocks which might impact other stores. This involved filtering those items from the grid.
$collection = Mage::getModel('cms/page')->getCollection();
$collection->addStoreFilter(Mage::helper('aitpermissions')->getStoreIds());
$conn = Mage::getSingleton('core/resource')->getConnection('core_read');
$page_ids = array();
foreach($collection as $key=>$item) {
$page_id = $item->getId();
$results = $conn->fetchAll("SELECT * FROM cms_page_store
WHERE page_id = ".$page_id.";");
$count = 0;
$arr_stores = array();
foreach($results as $row) {
$arr_stores[] = $row['store_id'];
$count++;
}
//We dont want to show the item if any of the following are true:
//The store id = 0 (Means its assigned to All Stores)
//There is more than one store assigned to this CMS page
if( in_array('0',$arr_stores) || $count>1) {
//This removes results from the grid (but oddly not the paging)
$collection->removeItemByKey($key);
}
else {
//build an array which we will use to remove results from the paging
$page_ids[] = $page_id;
}
}
//This removes results from paging (but not the grid)
$collection->addFieldToFilter('page_id',array('in'=>$page_ids));
I'm not sure why I needed to use two different methods to filter from the paging and the grid.
The site uses magento 1.5 so perhaps there is an issue related to that.
Either way, this solution worked for me.
My solution to add field store_id to pages collection via join, and use collection method addFieldToFilter().
$pages = Mage::getModel('cms/page')->getCollection();
$pages->getSelect()->joinInner(
array('cms_page_store' => 'cms_page_store'),
'main_table.page_id = cms_page_store.page_id',
array()
);
$pages->addFieldToFilter('store_id', ['in' => [1, 2]]);

Google calendar query returns at most 25 entries

I'm trying to delete all calendar entries from today forward. I run a query then call getEntries() on the query result. getEntries() always returns 25 entries (or less if there are fewer than 25 entries on the calendar). Why aren't all the entries returned? I'm expecting about 80 entries.
As a test, I tried running the query, deleting the 25 entries returned, running the query again, deleting again, etc. This works, but there must be a better way.
Below is the Java code that only runs the query once.
CalendarQuery myQuery = new CalendarQuery(feedUrl);
DateFormat dfGoogle = new SimpleDateFormat("yyyy-MM-dd'T00:00:00'");
Date dt = Calendar.getInstance().getTime();
myQuery.setMinimumStartTime(DateTime.parseDateTime(dfGoogle.format(dt)));
// Make the end time far into the future so we delete everything
myQuery.setMaximumStartTime(DateTime.parseDateTime("2099-12-31T23:59:59"));
// Execute the query and get the response
CalendarEventFeed resultFeed = service.query(myQuery, CalendarEventFeed.class);
// !!! This returns 25 (or less if there are fewer than 25 entries on the calendar) !!!
int test = resultFeed.getEntries().size();
// Delete all the entries returned by the query
for (int j = 0; j < resultFeed.getEntries().size(); j++) {
CalendarEventEntry entry = resultFeed.getEntries().get(j);
entry.delete();
}
PS: I've looked at the Data API Developer's Guide and the Google Data API Javadoc. These sites are okay, but not great. Does anyone know of additional Google API documentation?
You can increase the number of results with myQuery.setMaxResults(). There will be a maximum maximum though, so you can make multiple queries ('paged' results) by varying myQuery.setStartIndex().
http://code.google.com/apis/gdata/javadoc/com/google/gdata/client/Query.html#setMaxResults(int)
http://code.google.com/apis/gdata/javadoc/com/google/gdata/client/Query.html#setStartIndex(int)
Based on the answers from Jim Blackler and Chris Kaminski, I enhanced my code to read the query results in pages. I also do the delete as a batch, which should be faster than doing individual deletions.
I'm providing the Java code here in case it is useful to anyone.
CalendarQuery myQuery = new CalendarQuery(feedUrl);
DateFormat dfGoogle = new SimpleDateFormat("yyyy-MM-dd'T00:00:00'");
Date dt = Calendar.getInstance().getTime();
myQuery.setMinimumStartTime(DateTime.parseDateTime(dfGoogle.format(dt)));
// Make the end time far into the future so we delete everything
myQuery.setMaximumStartTime(DateTime.parseDateTime("2099-12-31T23:59:59"));
// Set the maximum number of results to return for the query.
// Note: A GData server may choose to provide fewer results, but will never provide
// more than the requested maximum.
myQuery.setMaxResults(5000);
int startIndex = 1;
int entriesReturned;
List<CalendarEventEntry> allCalEntries = new ArrayList<CalendarEventEntry>();
CalendarEventFeed resultFeed;
// Run our query as many times as necessary to get all the
// Google calendar entries we want
while (true) {
myQuery.setStartIndex(startIndex);
// Execute the query and get the response
resultFeed = service.query(myQuery, CalendarEventFeed.class);
entriesReturned = resultFeed.getEntries().size();
if (entriesReturned == 0)
// We've hit the end of the list
break;
// Add the returned entries to our local list
allCalEntries.addAll(resultFeed.getEntries());
startIndex = startIndex + entriesReturned;
}
// Delete all the entries as a batch delete
CalendarEventFeed batchRequest = new CalendarEventFeed();
for (int i = 0; i < allCalEntries.size(); i++) {
CalendarEventEntry entry = allCalEntries.get(i);
BatchUtils.setBatchId(entry, Integer.toString(i));
BatchUtils.setBatchOperationType(entry, BatchOperationType.DELETE);
batchRequest.getEntries().add(entry);
}
// Get the batch link URL and send the batch request
Link batchLink = resultFeed.getLink(Link.Rel.FEED_BATCH, Link.Type.ATOM);
CalendarEventFeed batchResponse = service.batch(new URL(batchLink.getHref()), batchRequest);
// Ensure that all the operations were successful
boolean isSuccess = true;
StringBuffer batchFailureMsg = new StringBuffer("These entries in the batch delete failed:");
for (CalendarEventEntry entry : batchResponse.getEntries()) {
String batchId = BatchUtils.getBatchId(entry);
if (!BatchUtils.isSuccess(entry)) {
isSuccess = false;
BatchStatus status = BatchUtils.getBatchStatus(entry);
batchFailureMsg.append("\nID: " + batchId + " Reason: " + status.getReason());
}
}
if (!isSuccess) {
throw new Exception(batchFailureMsg.toString());
}
There is a small quote on the API page
http://code.google.com/apis/calendar/data/1.0/reference.html#Parameters
Note: The max-results query parameter for Calendar is set to 25 by default,
so that you won't receive an entire
calendar feed by accident. If you want
to receive the entire feed, you can
specify a very large number for
max-results.
So to get all events from a google calendar feed, we do this:
google.calendarurl.com/.../basic?max-results=999999
in the API you can also query with setMaxResults=999999
I got here while searching for a Python solution;
Should anyone be stuck in the same way, the important line is the fourth:
query = gdata.calendar.service.CalendarEventQuery(cal, visibility, projection)
query.start_min = start_date
query.start_max = end_date
query.max_results = 1000
Unfortunately, Google is going to limit the maximum number of queries you can retrieve. This is so as to keep the query governor in their guidelines (HTTP requests not allowed to take more than 30 seconds, for example). They've built their whole architecture around this, so you might as well build the logic as you have.

Resources