I want to grab a user's uploads (ie: BBC) and limit the output to 10 per page.
Whilst I can use the following URL:
http://gdata.youtube.com/feeds/api/users/bbc/uploads/?start-index=1&max-results=10
The above works okay.
I want to use the query method instead:
The Zend Framework docs:
http://framework.zend.com/manual/en/zend.gdata.youtube.html
State that I can retrieve videos uploaded by a user, but ideally I want to use the query method to limit the results for a pagination.
The query method is on the Zend framework docs (same page as before under the title 'Searching for videos by metadata') and is similar to this:
$yt = new Zend_Gdata_YouTube();
$query = $yt->newVideoQuery();
$query->setTime('today');
$query->setMaxResults(10);
$videoFeed = $yt->getUserUploads( NULL, $query );
print '<ol>';
foreach($videoFeed as $video):
print '<li>' . $video->title . '</li>';
endforeach;
print '</ol>';
The problem is I can't do $query->setUser('bbc').
I tried setAuthor but this returns a totally different result.
Ideally, I want to use the query method to grab the results in a paginated fashion.
How do I use the $query method to set my limits for pagination?
Thanks.
I've decided just to use the user uploads feed as a way of getting pagination to work.
http://gdata.youtube.com/feeds/api/users/bbc/uploads/?start-index=1&max-results=10
If there is a way to use the query/search method to do a similar job would be interesting to explore.
I basically solved this in the same way as worchyld with a slight twist:
$username = 'ignite';
$limit = 30; // Youtube will throw an exception if > 50
$offset = 1; // First video is 1 (silly non-programmers!)
$videoFeed = null;
$uploadCount = 0;
try {
$yt = new Zend_Gdata_YouTube();
$yt->setMajorProtocolVersion(2);
$userProfile = $yt->getUserProfile($username);
$uploadCount = $userProfile->getFeedLink('http://gdata.youtube.com/schemas/2007#user.uploads')->countHint;
// The following code is a dirty hack to get pagination with the YouTube API without always starting from the first result
// The following code snippet was copied from Zend_Gdata_YouTube->getUserUploads();
$url = Zend_Gdata_YouTube::USER_URI .'/'. $username .'/'. Zend_Gdata_YouTube::UPLOADS_URI_SUFFIX;
$location = new Zend_Gdata_YouTube_VideoQuery($url);
$location->setStartIndex($offset);
$location->setMaxResults($limit);
$videoFeed = $yt->getVideoFeed($location);
} catch (Exception $e) {
// Exception handling goes here!
return;
}
The Zend YouTube API seems silly as the included getUserUploads method never returns the VideoQuery instance before it actually fetches the feed, and while you can pass a location object as a second parameter, it's an "either-or" situation - it'll only use the username parameter to construct a basic uri or only use the location, where you have to construct the whole thing yourself (as above).
Related
I’m working on a single-page-application with fully client-side rendered frontend (React) and Shopware acting as a headless CMS in the background. So all product-data will be pulled from the API and checkout will also use the API to send the data.
My problem is the following:
When trying to render the product list page, I call the articles endpoint which returns the basic info of all my products. The problem is that I would also need to render the main image associated with each product and this list does not expose any data from the media attached to the products.
I can get the media item(s) for a product using the Media endpoint, problem is that this one expects a Media Id which I cannot get from the listed representations of the articles.
So right now the only way I see to get the images is first making a call that gets all the products, then loop through them and get each product’s details, calling the article endpoint again with each product ID, then when I have the media IDs I loop through those and get the images for each product using the media endpoint, because that’s the only one that exposes the actual image path in the response. This seems way too complicated and slow.
Is there a smarter way to do this? Can I get Shopware to output the 1st image’s path in the Article list response, that’s associated with the current product?
Also I saw that the paths to the images look like this:
/media/image/f5/fb/95/03_200x200.jpg
The first part up to /media/image/ is fixed, that’s straight forward, and I get the image’s filename and extension in the article detail’s response in the media object and even the file extension, which looks like this in the API's response.
The problem is that I don’t know what’s the f5/fb/95/ stand for. If I could get this info from the details I could assemble the URL for the image programmatically and then I wouldn’t need the call to the media endpoint.
You can see how the path is generated in the class \Shopware\Bundle\MediaBundle\Strategy\Md5Strategy of Shopware's source code. The two relevant methods are:
<?php
public function normalize($path)
{
// remove filesystem directories
$path = str_replace('//', '/', $path);
// remove everything before /media/...
preg_match("/.*((media\/(?:archive|image|music|pdf|temp|unknown|video|vector)(?:\/thumbnail)?).*\/((.+)\.(.+)))/", $path, $matches);
if (!empty($matches)) {
return $matches[2] . '/' . $matches[3];
}
return $path;
}
public function encode($path)
{
if (!$path || $this->isEncoded($path)) {
return $this->substringPath($path);
}
$path = $this->normalize($path);
$path = ltrim($path, '/');
$pathElements = explode('/', $path);
$pathInfo = pathinfo($path);
$md5hash = md5($path);
if (empty($pathInfo['extension'])) {
return '';
}
$realPath = array_slice(str_split($md5hash, 2), 0, 3);
$realPath = $pathElements[0] . '/' . $pathElements[1] . '/' . join('/', $realPath) . '/' . $pathInfo['basename'];
if (!$this->hasBlacklistParts($realPath)) {
return $realPath;
}
foreach ($this->blacklist as $key => $value) {
$realPath = str_replace($key, $value, $realPath);
}
return $realPath;
}
Step for step, this happens generally to an image path, for example https://example.com/media/image/my_image.jpg, upon passing it to the encode-method:
The normalize method strips everything except for media/image/my_image.jpg
An md5-hash is generated out of the resulting string: 5dc18cdfa0...
The resulting string from step 1 is split at every /: ['media', 'image', 'my_image.jpg']
The first three character pairs from the md5-hash are being stored into an array: ['5d', 'c1', '8c']
The final path is being assembled out of the arrays from steps 3 and 4: media/image/5d/c1/8c/my_image.jpg
Every occurence of /ad/ is being replaced by /g0/. This is done, because there are ad-blockers which block requests to URLs containing /ad/
Shopware computes the path for you.
There is acutally no need to know about the hashed path...
You will be redirected to the correct path.
Try the following:
/media/image/ + image['path'] + image['extension']
An for the thumbnails:
/media/image/thumbnail/ + image['path'] + '_200x200' + image['extension']
or
/media/image/thumbnail/ + image['path'] + '_600x600' + image['extension']
I'm using insertRow to populate an empty spreadsheet, it starts off taking about 1 second to insert a row and then slows down to around 5 seconds after 150 rows or so.
Has anyone experienced this kind of behaviour?
There aren't any calculations on the data in the spreadsheet that could be getting longer with more data.
Thanks!
I'll try to be strict.
If you take a look at class "Zend_Gdata_Spreadsheets" you figure that the method insertRow() is written in a very not optimal way. See:
public function insertRow($rowData, $key, $wkshtId = 'default')
{
$newEntry = new Zend_Gdata_Spreadsheets_ListEntry();
$newCustomArr = array();
foreach ($rowData as $k => $v) {
$newCustom = new Zend_Gdata_Spreadsheets_Extension_Custom();
$newCustom->setText($v)->setColumnName($k);
$newEntry->addCustom($newCustom);
}
$query = new Zend_Gdata_Spreadsheets_ListQuery();
$query->setSpreadsheetKey($key);
$query->setWorksheetId($wkshtId);
$feed = $this->getListFeed($query);
$editLink = $feed->getLink('http://schemas.google.com/g/2005#post');
return $this->insertEntry($newEntry->saveXML(), $editLink->href, 'Zend_Gdata_Spreadsheets_ListEntry');
}
In short, it loads your whole spreadsheet just in order to learn this value $editLink->href in order to post new row into your spreadsheet.
The cure is to avoid using this method insertRow.
Instead, get your $editLink->href once in your code and then insert new rows each time by reproducing the rest of behaviour of this method. I.e, in your code instead of $service->insertRow() use following:
//get your $editLink once:
$query = new Zend_Gdata_Spreadsheets_ListQuery();
$query->setSpreadsheetKey($key);
$query->setWorksheetId($wkshtId);
$query->setMaxResults(1);
$feed = $service->getListFeed($query);
$editLink = $feed->getLink('http://schemas.google.com/g/2005#post');
....
//instead of $service->insertRow:
$newEntry = new Zend_Gdata_Spreadsheets_ListEntry();
$newCustomArr = array();
foreach ($rowData as $k => $v) {
$newCustom = new Zend_Gdata_Spreadsheets_Extension_Custom();
$newCustom->setText($v)->setColumnName($k);
$newEntry->addCustom($newCustom);
}
$service->insertEntry($newEntry->saveXML(), $editLink->href, 'Zend_Gdata_Spreadsheets_ListEntry');
Don't forget to encourage this great answer, it costed me few days to figure out. I think ZF is great however sometimes you dont want to rely on their coode too much when it comes to resources optimization.
ZF 1.11.5 is puking all over this search function. i've tried creating the query several different ways, sent the sql statement to my view, copied and pasted the sql statement into phpMyAdmin and successfully retrieved records using the sql that ZF is choking on. i have been getting a coupld of different errors: 1) an odd SQL error about 'ordinality' (from my Googling ... it seems this is a ZF hang up .. maybe?) and 2) Fatal error: Call to undefined method Application_Model_DbTable_Blah::query() in /blah/blah/blah.php on line blah
public function searchAction($page=1)
{
$searchForm = new Application_Model_FormIndexSearch();
$this->view->searchForm = $searchForm;
$this->view->postValid = '<p>Enter keywords to search the course listings</p>';
$searchTerm = trim( $this->_request->getPost('keywords') );
$searchDb = new Application_Model_DbTable_Ceres();
$selectSql = "SELECT * FROM listings WHERE `s_coursedesc` LIKE '%".$searchTerm."%' || `s_title` LIKE '%".$searchTerm."%'";
$selectQuery = $searchDb->query($selectSql);
$searchResults = $selectQuery->fetchAll();
}
here's my model ....
class Application_Model_DbTable_Ceres extends Zend_Db_Table_Abstract
{
protected $_name = 'listings';
function getCourse( $courseId )
{
$courseid = (int)$courseId;
$row = $this->fetchRow('id=?',$courseId);
if (!$row)
throw new Exception('That course id was not found');
return $row->toArray();
}
}
never mind the view file ... that never throws an error. on a side note: i'm seriously considering kicking ZF to the curb and using CodeIgniter instead.
looking forward to reading your thoughts. thanks ( in advance ) for your responses
You're trying to all a method called query() on Zend_Db_Table but no such method exists. Since you have built the SQL already you might find it easier to call the query on the DB adapter directly, so:
$selectSql = "SELECT * FROM listings WHERE `s_coursedesc` LIKE '%".$searchTerm."%' || `s_title` LIKE '%".$searchTerm."%'";
$searchResults = $selectQuery->getAdapter()->fetchAll($selectSql);
but note that this will give you arrays of data in the result instead of objects which you might be expecting. You also need to escape $searchTerm here since you are getting that value directly from POST data.
Alternatively, you could form the query programatically, something like:
$searchTerm = '%'.$searchTerm.'%';
$select = $selectQuery->select();
$select->where('s_coursedesc LIKE ?', $searchTerm)
->orWhere('s_title LIKE ?', $searchTerm);
$searchResults = $searchQuery->fetchAll($select);
I want to paginate search results using Zend_Paginator. So I pass my data to a paginator instance:
$paginator = new Zend_Paginator (
new Zend_Paginator_Adapter_DbSelect ( $data )
);
Data is returned this way
public function getData($idArray){
$db = Zend_Db_Table::getDefaultAdapter();
$selectProgramme = new Zend_Db_Select($db);
$selectProgramme->from('programme')
->order('id DESC')
->where('id IN(?)', $idArray);
return $selectProgramme;
}
$idArray is provided by my search implementations. This all works great and I get the correct data and pagination links displayed.
However I can't paginate the result because the pagination links are not valid. So normal pagination would have following link:
mysite.de/home/index/page/1
in search I now have
mysite.de/home/search/page/1
This does not work. Any suggestions how to implement search pagination?
EDIT: I have a HomeController with two actions, index and search action. IndexAction displays all data and I can paginate it.
public function indexAction(){
//...
$paginator = new Zend_Paginator(
new Zend_Paginator_Adapter_DbSelect($data)
);
$paginator->setItemCountPerPage(16)
->setPageRange(20)
->setCurrentPageNumber($this->_getParam('page', 1));
$this->view->data = $paginator;
}
The searchActions handles the search process:
public function searchAction(){
$response = $solr->search($this->getRequest()->getParam('search', null));
//...if items found get the data exactly the same way as in the
// index action, using Zend_Paginator_Adapter_DbSelect
$paginator = new Zend_Paginator(
new Zend_Paginator_Adapter_DbSelect($data)
);
$paginator->setItemCountPerPage(16)
->setPageRange(20)
->setCurrentPageNumber($this->_getParam('page', 1));
$this->view->data = $paginator;
}
So like you see in the search action there is a problem with the search process when I paginate. I need to decide somehow if to search or to paginate. Any suggestions on that?
Since search required the search parameter pagination will fail because when paginating the the search parameter is null.
$sreq = $this->getRequest()->getParam('search', null);
So we need to pass this parameter whenever we paginate our search. I solve this using Zend_Session:
//get search param
$sreq = $this->getRequest()->getParam('search', null);
//store search param in session for pagination
$search = new Zend_Session_Namespace('PSearch');
if($sreq != null){
$search->psearch = $sreq;
}else{
$sreq = $search->psearch;
}
I have this at the top of my searchAction and everything works.
Not sure I understand, but is your problem that the page parameter from the url is not making it's way to the Paginator - e.g. regardless of what page you are on, it is always showing the first 20 results?
If so, have you tried manually setting the page on the paginator:
$page = $this->_getParam('page', 1);
$paginator->setCurrentPageNumber($page);
public function search()
Are you sure that you didn't mistyped here? Should be
public function searchAction()
You put your search data into $response but create paginator instance using $data (which is null)
I've been using Zend Framework for a few months now. So, my knowledge is pretty good but I'm not quite an expert yet. I am trying to use zend_lucene with zend_paginator and so far not successful. I am able to use zend_lucene and index data successfully by itself and able to do use zend_paginator when querying the database, but I can't seem to combine the two. Here is a sample of what I am doing:
try {
$searchresults = $index->find($lucenequery);
}
catch (Zend_Search_Lucene_Exception $e) {
echo "Unable {$e->getMessage()}";
}
$page = $this->_getParam('page',1);
$paginator = Zend_Paginator::factory($searchresults);
$paginator->setItemCountPerPage(20);
$paginator->setCurrentPageNumber($page);
$this->view->paginator = $paginator;
Is there a different step I need to do with lucene and zend_paginator? I am really uncertain. The result I get is that for the first page results display properly. But when I hit the second page or third my results are blank. So uncertain what might be wrong as I can't find docs or tutorials in using the two together. Any help would be greatly appreciated.
I think this may work with the iterator adapter:
public function searchAction() {
$index = Zend_Search_Lucene::open('/path/to/lucene');
$results = $index->find($this->_getParam('q'));
$paginator = Zend_Paginator::factory($results);
$paginator->setCurrentPageNumber($this->_getParam('page', 1));
$paginator->setItemCountPerPage(10);
$this->view->results = $paginator;
}
Perhaps the problem you are having is that $paginator doesn't know how many search results there are..
So you may need to do that manually:
$paginator->setDefaultPageRange($results->count());