Requesting Image Assets from external Api using a proxy - php

I am trying to access images from an external API using guzzle in laravel, however, as understandably the calls are becoming really expensive and have a drastic effect on page loading time. I know that a proxy call would solve the issue, however, is there any way of achieving that in laravel where a certain endpoint can act as a proxy and can return an image. Thanks

I found a solution to my own problem and will post an answer. Mind you, there are better solutions i.e. configuring your server to handle such calls. However, in context of Laravel, here is what I did.
The data I had to work with
[
"orientation" => "portrait"
"fig_type" => "diagram"
"id" => "A_SDEF-05-2016-0003001"
"position" => "float"
"label" => "Figure 1."
"caption" => "some caption"
"link" => "/resource/id/A_SDEF-05-2016-0003001.tif"
]
Solution :
Step 1
Loop through the data using collections in Laravel as in its function;
// Pass the link as a get request parameter to laravel route
// i.e. localhost/proxy/image?link=resource/id/A_SDEF-05-2016-0003001.tif
return collect($images)->each(function ( $image )
{
if ($image['link']) {
$image['link'] = ltrim($image['link'],'/');
return $image['link'] ="link={$image['link']}";
}
return $image;
});
Step 2
Register an endpoint in routes file e.g.
Route::get('/proxy/image', 'ProxyController#request_image');
Step 3
The controller method i.e. request_image makes a Guzzle request and return the response as:
$link = request('link');
return \Response::stream(function() use($image){
echo $image;
},200, [$headers]);
Step 4
Have a static function or a regular function to output some dynamic html that you will build however in img src you would call ProxyController image method i.e.
$img = '<img src="'. action('ProxyController#request_image',$image['link']).'") >";
-Step 5
Output the html in blade
{!! Asset::image($image) !!} // Just output the built html
Hope it helps :D

Related

Why when I return a string value from database, it gives me a string with 'http://localhost:8000' in it?

I save a featured photo to my public/uploads/post and save a string with the photo name to my database. And when I want to delete it, I can't do it. Then I track the error and just realize when I return the value of my photo field in the table, it gives me the photo name with http://localhost:8000 in it
This is my code :
$featured = $request->featured;
$featured_new_name = time() . $featured->getClientOriginalName();
$featured->move('uploads/post', $featured_new_name);
$post = Post::create([
'post_title' => $request->post_title,
'post_content' => $request->post_content,
'featured' => 'uploads/post/' . $featured_new_name,
'category_id' => $request->category_id,
'post_slug' => str_slug($request->post_title)
]);
Then when i try to return it with die() :
die($post->featured);
It give me this result :
http://localhost:8000/uploads/post/1545856404foto.jpg
When i die() it, before save to the database, it work fine :
die($featured_new_name);
The Result :
1545856533foto.jpg
I return another value after i save to database, and it work fine :
die($post->post_title);
The Result :
asdsadadsada
So, before i save to the database, the photo name work fine. But after i save to database and return it, the photo name mess up. That the database work like that? Or it is an error?
It seems like your system has some extra programmatic logic in it that either
Adds http://localhost:8000 to the value before saving it
Add http://localhost:8000 to the variable (via a magic __geter) before returning the value of post_title
This may be something that's in your Post class itself, or may be happening in an event listener. Unfortunately, without more information about your system it's impossible to say for sure what the problem is.
try to save just the name of the photo :
'featured' => $featured_new_name,
and instead use the full photo address in the href attribute of your img tag.

CakePhp sessions clear almost instantly, but only sometimes

I have adopted a CakePhp 2 project. We get to the project from another project, linking to the CakePhp project with a "token" and a conference ID as a parameter in the URL. Using that token, we authorize the user, and using the conference ID get the information from the database. The session value "auth" is set to true.
We have it running on 2 "platforms" locally on my system using a vagrant machine, and on a production server. Locally the session value dies really quick and at random times. On the production server not as often, but the issues we have where Ajax calls don't seem to do what are expected, we believe are being caused by a similar issue. We have many different projects, all Laravel, with zero issues where the session values clear. This issue is strictly with the CakePhp project.
All the authentication magic happens in the beforeFilter method. The code:
public function beforeFilter() {
$session = new CakeSession();
/**
*
* We will check if the current user is authorized here!
*
*/
// If the visitor is coming for the first time, there should be a parameter in
// the URL that is the auth code to check against the database.
if ( ( isset($_GET['conf']) && is_numeric($_GET['conf']) ) && isset($_GET['token']) ) {
$getConference = ClassRegistry::init('Conference')->find('first', ["conditions" => ["conference_id"=>$_GET['conf'] ]]);
$checkToken = ClassRegistry::init('User')->find('first', ["conditions" => ["remember_token"=>$_GET['token'] ]]);
if ($getConference && $checkToken) {
$checkToken['User']['remember_token'] = $this->generateToken();
if ( ClassRegistry::init('User')->save( $checkToken ) ) {
$session->write('auth', true);
$session->write('conferenceId', $_GET['conf']);
$this->redirect('/');
}
}
else {
$session->write('auth', false);
$session->write('conferenceId', null);
}
}
if (! $session->read('auth') || $session->read('conferenceId') == null ) {
echo "No permission!";
exit;
}
}
At the top of the controller:
App::uses('CakeSession', 'Model/Datasource');
When the URL parameters are present, it traps them, does the work, and redirects to the home route without the parameters.
$this->generateToken();
Creates a new token, and overwrites the old one in the database.
There are 2 main controllers. The controller with this code is the main projects controller. The only time it is really hit is the first time you go to the project, and we hit the index method. From there everything else is AJAX calls to the other controller. There is one link, a "home" type link that will hit that index method.
Sometimes these Ajax calls stop working, and clicking that home link will output "No Permission" instead of the expected html in the container the Ajax call outputs too.
Steps to troubleshoot led me to putting this beforeFilter method on the top of the second controller. Now, randomly I'll get no permission. Sometimes, when I'm on the main project that links to this CakePhp project, I click that link, I get no permission right off the bat.
I found this page: cakephp takes me to login page on multiple request and have tried to set the session details like this:
Configure::write('Session', array(
'defaults' => 'php',
'timeout' => '300' // <- added this element
));
And I have tried:
Configure::write('Session.timeout', '300');
Additionally, I have tried cookieTimeout in both of those cases.
I've also tried
Configure::write('Security.level', 'low');
and included
Configure::write('Session.autoRegenerate', true);
In any order, any of these cause the session to bomb out immediately. I get "No permission on page load, and never get anywhere.
The code for this project is honestly crap. The developer who wrote it had mistakes and errors all over the place. On top of that, we are a Laravel shop. We are just trying to keep the project limping along until sometime in the future when we can nuke it from orbit. So we just need to get this working. Any thoughts on what could be causing this? Any other details I am forgetting to include that would help troubleshoot this issue?
Thanks
Reading & writing session data
You can read values from the session using Set::classicExtract() compatible syntax:
CakeSession::read('Config.language');
$key should be the dot separated path you wish to write $value to:
CakeSession::write('Config.language', 'eng');
When you need to delete data from the session, you can use delete:
CakeSession::delete('Config.language');
You should also see the documentation on Sessions and SessionHelper for how to access Session data in the controller and view.

Laravel 5: To cache or use sessions for building a site-wide banner?

I'm building a feature into a Laravel 5 app that will allow you to set the content of a status banner that will display across the top of the page. We will be using this banner both to display page-specific things (status messages, etc) and site-wide announcements (every user sees the same thing, banner stays the same for awhile).
Right now, I've implemented this by using Laravel sessions to allow banners to be added by calling a helper method from any controller or middleware:
// Call set_banner from in a controller or middleware (for persistent banners)
function set_banner($banner_text, $banner_class, $banner_persistant=false, $replace=false)
{
$banners = session()->get('banners', []);
// Create new banner
$banner = [
'text' => $banner_text,
'type' => $banner_class,
'persistent' => $banner_persistant
];
// Only put banner in array if it's not already there
if( !in_array($banner, $banners) ) {
// Either override existing banners, or add to queue
if( !$replace ) session()->push('banners', $banner);
else session()->put('banners', [$banner]);
}
}
// Called by default in the master.blade.php template
function get_banners()
{
$banners = session()->pull('banners', Array());
foreach( $banners as $banner ) {
// Print out each banner
print '<div class="col-md-12"><div class="text-center alert alert-block alert-'.$banner['type'].'">';
print $banner['text'];
print '</div></div>';
// Push back into the session if banner is marked as persistent
if ( $banner['persistent'] ) session()->push( 'banners', $banner );
}
}
Banners are created in controllers or middleware like this:
set_banner("<b>Note:</b> This is a sample persistant-scope banner set in a controller", "success", true);
Is there a better way to accomplish storing both page-level and site-wide banners? My concerns is that hitting the session on every pageload may be inefficient, especially for banners that won't be changing for long periods of time. Will this approach mess with Laravel's cache, etc?
As you said the banners do not change that often. Hence for me i would implement it using Cache. This improves performance since we need only one use to have the banners cached. And for the rest its retrieved faster from the Cache rather Session.
Do you want to have to change code to change the banner of a given page?
I would suggest instead creating a "pages" package, where each page route name is entered into a database.
From there, from your page service provider you get Page::getModel()->banner_text or something similar.
The method would look for a db result matching the current route name with a result within db.
when a controller method is triggered you simply call
Page::getBannerText()
That method will pull the current route name, pull the page result related to that page if it exists or create it if it does not exist (easy way to get everything). You cache the db query result for X hours, days or whatever so whenever someone else makes a call, you don't even need to deal with any storage on client side.
This allows you to modify the value from a db fascet. Its the more "proper" way to do it.

Instagram API to fetch all photo in php

i'm using this api:
public function getUserMedia($id = 'self', $limit = 0) {
return $this->_makeCall('users/'.$id.'/media/recent', true, array('count' => $limit));
}
to fetch the photo of a user logged in my site using php
it works, and all other api works.
the problem is that i want to retrieve ALL the photo of a user (such as printstagr.am).
i've searched in the api but without success: it seems that you can take the recents or the populars, but the site mentioned above takes all. any idea?
thanks!
Not sure what the max count is (saw someone mention 20 was the max on another question, but can't find any limit in the docs from a quick scan), but essentially what you have to do is request as many as possible, then follow the pagination links to collect more.
So from the api docs they provide this:
{
...
"pagination": {
"next_url": "https://api.instagram.com/v1/tags/puppy/media/recent?access_token=fb2e77d.47a0479900504cb3ab4a1f626d174d2d&max_id=13872296",
"next_max_id": "13872296"
}
}
Your application needs to store the objects from the request (i.e. an array), then fire a new request to the "next_url", put those objects into the same store (i.e. array), then follow the link again, until you reach the end or until you get enough to satisfy your needs.

Twitter typeahead not matching correctly when using "remote", but working with "local" JSON

I'm creating an autocomplete in Laravel 4 using twitter typeahead, but I'm having problems in the matching process.
The JS code:
$('#autocomplete').typeahead({
limit: 20,
name: 'destinatari',
prefetch: '{{URL::to("alumni/posta/utenti")}}',
remote: '{{URL::to("alumni/posta/utenti")}}'
});
The model populates the array like this:
public static function jsonList($idLogged)
{
$users = DB::table('users_data')
->where('userid', '!=', $idLogged)
->select('nome', 'nome_2', 'cognome', 'cognome_2', 'userid')
->orderBy('cognome', 'asc')
->orderBy('nome','asc')
->get();
$i = 0;
$ordered = array();
foreach($users as $u) {
$ordered[$i]['value'] = $u->nome.' '.$u->nome_2.' '.$u->cognome.' '.$u->cognome_2;
$ordered[$i]['tokens'] = array($u->nome, $u->cognome);
$ordered[$i]['userid'] = $u->userid;
$i++;
}
return $ordered;
}
And my controller simply:
return Response::json( Users::jsonList($this->userdata->id) );
The returned json (I see it in Firebug) looks like:
[{"value":"Silvia Miriam Abeyta Carretero","tokens":["Silvia","Abeyta"],"userid":"246"},
{"value":"Malak Julia Abreu Garrido","tokens":["Malak","Abreu"],"userid":"198"},{"value":"Aina Aguado ","tokens":["Aina","Aguado"],"userid":"243"},
{"value":"Jordi Alarc\u00f3n ","tokens":["Jordi","Alarc\u00f3n"],"userid":"308"},
{"value":"Aaron Nerea Alejandro ","tokens":["Aaron","Alejandro"],"userid":"49"},
{"value":"Alexia Alem\u00e1n ","tokens":["Alexia","Alem\u00e1n"],"userid":"306"},
{"value":"Salma Almaraz ","tokens":["Salma","Almaraz"],"userid":"54"},
{"value":"Alma Almonte Nev\u00e1rez","tokens":["Alma","Almonte"],"userid":"101"},
{"value":"Daniela Almonte ","tokens":["Daniela","Almonte"],"userid":"184"}
,....other similar results....]
The problem is that any letter I type only the first name in the list gets autocompleted ("Silvia Miriam Abeyta Carretero") in the input field, but when I type out in full any other name (say, "Daniela Almonte") the field isn't completed and the dropdown keeps showing the whole 20 results, without processing of any kind.
I must say, though, that if I click on a name it gets selected correctly (I'm logging the userid property of the datum ), but still the autocompletion isn't working.
What puzzles me is that If I copy/paste the whole list directly in the JS (taken as is from Firebug), as local property of typeahead() (instead of remote), everything works fine as it should.
What could be causing this? Is it a Laravel response problem? Or is it the typeahead processor (I believe it's in "transport.js" source file, uses jQuery $.ajax()) of the remote url?
It seems like the returned JSON is considered as a single entry, i don't know why since it looks correct to me...
From the typeahead documentation:
$('input.twitter-search').typeahead({
name: 'accounts',
prefetch: 'https://twitter.com/network.json',
remote: 'https://twitter.com/accounts?q=%QUERY'
});
As you can see, the remote parameter must contains the URL with the query parameter.
PHP side, you have to test if the parameter "q" is set and then adapt your request (with a LIKE statement by example) to correctly filter the list.
Otherwise, you can try to remove this remote parameter, maybe it would work with only the prefetch attribute.

Categories