I am trying to access and load a PDF in the browser using Laravel for the first time. Not sure what I am doing wrong.
I have the following route in my web.php:
use Illuminate\Http\Response;
Route::get('public/downloads/{pathToFile}', function ($pathToFile) {
return response()->file($pathToFile);
});
I have the following in my view:
<a class='col-6' href="/downloads/{{ asset('public/storage/'
. $plansubmission->current_plan_year_claims_data_file_1) }}">{{
asset('storage/' . $plansubmission-
>current_plan_year_claims_data_file_1) }}</a>
From the docs: The public disk is intended for files that are going to be publicly accessible. By default, the public disk uses the local driver and stores these files in storage/app/public.
This is assuming you have created a symbolic link by running php artisan storage:link. Otherwise, you might need to tweak this for wherever you are storing the PDF, but I think this approach accomplishes what you're after.
Routes/web.php
Route::get('/public/download/{filename}', 'PdfController#download')->name('download.pdf');
PdfController
use Illuminate\Support\Facades\Storage;
/**
* Display a PDF directly in the browser
*
* #param $filename <-- the filename we want
*
* #return Response
**/
public function download($filename)
{
$url = $filename.'.pdf';
if( $this->storageObjectExists('public', $url) == true ) // Does this file exist?
{
return response()->file($url); // <-- display file directly in browser
} else {
return back()->with('danger', 'No file located!'); // Let the user know
}
}
/**
* Determine if a particular file exists on a disk
*
* #param $disk <-- 'local', 'azure', 'aws', etc
* #param $url <-- /path/to/file.pdf
*
* #return boolean
**/
public function storageObjectExists($disk, $url)
{
$exists = Storage::disk($disk)->exists($url);
return $exists;
}
Form.blade.php
<!-- optional warning for user -->
#if(session('danger'))
<div class="alert alert-danger">
<p>{{ session('danger') }}</p>
</div>
#endif
{{ $filename }}
Related
Here's my current issue.
At the moment, I have a page with elements that can be added in and appended via AJAX. These elements contain forms, image uploads etc.
I have a middleware on my entire application that checks the size of any image being uploaded at any given time and makes sure its under 5MB (image validation for each image upload on the application is not an option, it has to be 1 controller that maintains all image upload validation).
If the request detects an image thats over 5MB, it will run this code
return redirect()->back()->withInput($request->all())->withErrors(array('Image' => 'Sorry, ' . $file->getClientOriginalName() . ' is too large, maximum file size is 5MB. Please reduce the size of your image!'));
This code is very temperamental, and heres why.
I need the page to be in the EXACT same state i left it in, when its returned. That means all AJAX loaded elements, all images, everything needs to be in the same state, so redirect()->back()->withInput($request->all()) doesn't work, because it still refreshes the page and removes everything loaded appended and added in that instance.
I need to be able to cancel the request if it fails.
In plain english, When this middleware is ran, detect all images. If there is an image that is over 5MB, do not refresh the page or anything. Just error
I know this seems silly, because the request cannot pass something back without refreshing, but I thought id ask / open to suggestions.
Here's my middleware
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\UploadedFile;
use Symfony\Component\HttpFoundation\Response;
class ImageInterceptor
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
foreach (array_filter(array_flatten($request->files->all())) as $file) {
//Check if the file being uploaded is not a csv
if($file->getClientOriginalExtension() != 'csv'){
$filename = $file->getClientOriginalName();
$size = $file->getClientSize(); // size in bytes!
$onemb = pow(1024, 2);
if ($size > $onemb * 5) {
//Return back, image is too big!
return redirect()->back()->withInput($request->all())->withErrors(array('Image' => 'Sorry, ' . $file->getClientOriginalName() . ' is too large, maximum file size is 5MB. Please reduce the size of your image!'));
}
}
}
return $next($request);
}
}
If you plan on having the page on the same state, then you can't tell it to redirect backwards with errors, you would have to return an array, or string, or whatever is your needs. By saying redirect backwards, it's telling the browser where to navigate.
About maintaining the inputs, you can try something among these lines:
<input type="text" name="firstname" id="firstname" class="form-control" value="{{ $user->firstname or old('firstname') }}">
Why don't you create a form request? I really doubt you need that validation for every page you require. Middleware, in my opinion, should handle authentication and authorization .
A formrequest would be something like:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class Example extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'photo' => 'required|mimes:jpeg,bmp,png|size:5000'
];
}
}
And on your controller, you just place a argument on the function (instead of Request $request, you place Example $request). This way, you can access every request information that Illuminate has plus your own validation.
https://laravel.com/docs/5.2/validation#rule-mimetypes
I am trying to load the stylesheet in my Assets folder following the instruction giving on this page:
https://symfony.com/doc/current/best_practices/web-assets.html
But I fail to navigate from the base.html.twig to the folder using:
<link rel="stylesheet" type="text/css" href="/assets/css/style.css" />
my file structure looks like this:
templates
base.html.twig
assets
css
style.css
Any ideas why this doesn't work?
You can try to use symfony's asset function:
<link rel="stylesheet" href="{{ asset('css/style.css') }}" />
In your AppExtension:
// register function here
public function getFunctions() {
return [
new TwigFunction('load_js_asset', array($this, 'fileGetJsAsset')),
];
}
// register filter here
public function getFilters() : array
{
return [
new TwigFilter('html_entity_decode',
[$this, 'html_entity_decode'],
['is_safe' => ['html']]),
}
/**
* #param string $string
* #return string
*/
public function html_entity_decode(string $string) : string {
return html_entity_decode($string);
}
/**
* #param string $file
* #param bool $embedScriptTag
* #return string
*/
public function fileGetJsAsset(string $file, bool $embedScriptTag = true) : string {
/** #var bool|string $content */
$content = file_get_contents(__DIR__ . '/../../assets/js/' . $file);
if ($content === false) {
throw new FileException('Could not found or read file: ' . $file);
}
if (!$embedScriptTag) {
return $content;
}
return '<script>' . $content . '</script>';
}
Now in your view load js assets like this:
// loading from root assets/js folder
{{ load_js_asset('core/jquery.min.js', true)|html_entity_decode }}
{{ load_js_asset('core/popper.min.js')|html_entity_decode }}
{{ load_js_asset('my-beauty-js-file.js')|html_entity_decode }}`
I hope it helps.
Maybe you will need also htmlspecialchars_decode filter.
http://symfony.com/doc/3.4/best_practices/web-assets.html
Assets function still being usefull in Symfony 4.
A couple of possible solutions - without more detail I can't be sure if they will definitely solve the problem or not:
First, did you configure webpack-encore and run the build? Without that the resources won't be there and nginx will generate a 404 trying to route to it.
Second, if you're running with Docker, you may not have mounted the ./app/public/ folder onto the nginx container so that the files could be served. If you are getting a Symfony 404 when you go to those files, this may be the issue.
I want to display the image that stored in public folder on Lumen project.
But I got 404 not found when try to display it in browser.
I already create the asset url helper:
if (!function_exists('urlGenerator')) {
/**
* #return \Laravel\Lumen\Routing\UrlGenerator
*/
function urlGenerator() {
return new \Laravel\Lumen\Routing\UrlGenerator(app());
}
}
if (!function_exists('asset')) {
/**
* #param $path
* #param bool $secured
*
* #return string
*/
function asset($path, $secured = false) {
return urlGenerator()->asset($path, $secured);
}
}
and this is how I call the image URL :
asset('public/uploads/images/product/') . $product->image
why lumen referring to route when I try to call image in public folder ?
You can do like the below.
asset('uploads/images/product/' . $product->image)
or
asset('uploads/images/product/') . '/' . $product->image
The asset() URL helper already generates a path to your root folder (which is inside the public fodler) and so you don't need to include it.
Using:
asset('uploads/images/product/' . $product->image)
or
asset('uploads/images/product/') . $product->image
should work just fine for you.
Remember you can always look at the link that gets generated to see what your helper is generating - and tweak it as required.
On Owncloud 8.1 using the owncloud command line, I create a new test app:
ocdev startapp MyApp --email mail#example.com --author "Your Name" --description "My first app" --owncloud 8
The app is working, I can add it in the owncloud control panel.
Now I'd like to write to a file, so I use one example from the owncloud documentation:
https://doc.owncloud.org/server/8.1/developer_manual/app/filesystem.html
[Edit] I started over and now, I don't know if I omitted something, but "myapp" comes with no "application.php" file.
So I create it at /var/www/core/apps/myapp/appinfo/application.php :
<?php
namespace OCA\MyApp\AppInfo;
use \OCP\AppFramework\App;
use \OCA\MyApp\Storage\AuthorStorage;
class Application extends App {
public function __construct(array $urlParams=array()){
parent::__construct('myapp', $urlParams);
$container = $this->getContainer();
/**
* Storage Layer
*/
$container->registerService('AuthorStorage', function($c) {
return new AuthorStorage($c->query('RootStorage'));
});
$container->registerService('RootStorage', function($c) {
return $c->query('ServerContainer')->getRootFolder();
});
}
}
Then I create a file called /var/www/core/apps/myapp/storage/AuthorStorage.php with:
<?php
namespace OCA\MyApp\Storage;
class AuthorStorage {
private $storage;
public function __construct($storage){
$this->storage = $storage;
}
public function writeTxt($content) {
// check if file exists and write to it if possible
try {
try {
$file = $this->storage->get('/myfile.txt');
} catch(\OCP\Files\NotFoundException $e) {
$this->storage->touch('/myfile.txt');
$file = $this->storage->get('/myfile.txt');
}
// the id can be accessed by $file->getId();
$file->putContent($content);
} catch(\OCP\Files\NotPermittedException $e) {
// you have to create this exception by yourself ;)
throw new StorageException('Cant write to file');
}
}
}
The sample app already gives me a route to the index function in the pagecontroller.php
['name' => 'page#index', 'url' => '/', 'verb' => 'GET']
How do I call the function "writeTxt" from there?
Based on http://php.net/manual/en/language.oop5.basic.php
I tried:
use \OCA\MyApp\Storage\AuthorStorage;
and
public function index() {
//added part
$a = new AuthorStorage();
$a->writeTxt('test');
//original part
$params = ['user' => $this->userId];
return new TemplateResponse('myapp', 'main', $params); //templates/main.php
}
After running I get a "Class 'OCA\MyApp\Storage\AuthorStorage' not found at /var/www/core/apps/myapp/controller/pagecontroller.php#44"
Even with the help of use \OCA\MyApp\Storage\AuthorStorage; ( ClassNotFoundException: Attempted to load class... Symfony ) it doesn't seem to help.
Thanks
Time has gone by, so I'm posting an answer to my question for owncloud 9.
Here are the steps to a basic script with read, write, copy file ability.
"application.php" has definitively been removed from the example. It was not a bug.
Following:
https://doc.owncloud.org/server/9.0/developer_manual/app/startapp.html
Generate the demo "MyApp" app:
ocdev startapp MyApp --email mail#example.com --author "Your Name" --description "My first app" --owncloud 9
Edit the file myapp/controller/pagecontroller.php
<?php
/**
* ownCloud - myapp
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* #author Your Name <mail#example.com>
* #copyright Your Name 2016
*/
namespace OCA\MyApp\Controller;
use OCP\IRequest;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Controller;
use OCP\Files\IRootFolder;
use OC\Files\Storage\Temporary;
class PageController extends Controller {
private $userId;
private $storage;
private $userstorage;
public function __construct($AppName, IRequest $request, IRootFolder $storage, $UserId){
parent::__construct($AppName, $request);
$this->storage = $storage;
$this->userId = $UserId;
$this->userstorage = $this->storage->get($this->userId.'/files/');
}
/**
* CAUTION: the #Stuff turns off security checks; for this page no admin is
* required and no CSRF check. If you don't know what CSRF is, read
* it up in the docs or you might create a security hole. This is
* basically the only required method to add this exemption, don't
* add it to any other method if you don't exactly know what it does
*
* #NoAdminRequired
* #NoCSRFRequired
*/
public function index() {
$listedudossier = $this->userstorage->getDirectoryListing();
//list all items in the root directory
//and copies the files in an directory called old
//the directory "old" is not script created
foreach ($listedudossier as $value) {
if ( $value->getType() == 'file' ){
$value->copy($this->userId.'/files/old/'.$value->getName());
//also works
//$value->copy($this->userstorage->getPath().'/old/'.$value->getName());
}
}
$params = ['listedudossier' => $listedudossier ];
return new TemplateResponse('myapp', 'main', $params); // templates/main.php
}
/**
* Simply method that posts back the payload of the request
* #NoAdminRequired
*/
public function doEcho($echo) {
//creates a file
$this->userstorage->newFile('myfile2.txt');
//opens a file, adds inputbox content and saves the file
$file = $this->userstorage->get('myfile.txt');
$contenu = $file->getContent();
$file->putContent($contenu.$echo);
return new DataResponse(['echo' => $echo]);
}
}
You also need to edit the myapp/templates/part.content.php
<p>Hello World <?php p($_['user']) ?></p>
<p><button id="hello">click me</button></p>
<p><textarea id="echo-content">
Send this as ajax
</textarea></p>
<p><button id="echo">Send ajax request</button></p>
Ajax response: <div id="echo-result"></div>
<p>listedudossier:<p> <?php
foreach ($_['listedudossier'] as $file) {
echo "type:".$file->getType();
echo "<p>";
echo "nom:".$file->getName();
echo "<p>";
echo "modif:".$file->getMTime();
echo "<p>";
}
From here you can test the code in a development environment:
https://github.com/owncloud/ocdev/blob/master/README.rst#installation
How can I compile a blade template from a string rather than a view file, like the code below:
<?php
$string = '<h2>{{ $name }}</h2>';
echo Blade::compile($string, array('name' => 'John Doe'));
?>
http://paste.laravel.com/ujL
I found the solution by extending BladeCompiler.
<?php namespace Laravel\Enhanced;
use Illuminate\View\Compilers\BladeCompiler as LaravelBladeCompiler;
class BladeCompiler extends LaravelBladeCompiler {
/**
* Compile blade template with passing arguments.
*
* #param string $value HTML-code including blade
* #param array $args Array of values used in blade
* #return string
*/
public function compileWiths($value, array $args = array())
{
$generated = parent::compileString($value);
ob_start() and extract($args, EXTR_SKIP);
// We'll include the view contents for parsing within a catcher
// so we can avoid any WSOD errors. If an exception occurs we
// will throw it out to the exception handler.
try
{
eval('?>'.$generated);
}
// If we caught an exception, we'll silently flush the output
// buffer so that no partially rendered views get thrown out
// to the client and confuse the user with junk.
catch (\Exception $e)
{
ob_get_clean(); throw $e;
}
$content = ob_get_clean();
return $content;
}
}
Small modification to the above script.
You can use this function inside any class without extending the BladeCompiler class.
public function bladeCompile($value, array $args = array())
{
$generated = \Blade::compileString($value);
ob_start() and extract($args, EXTR_SKIP);
// We'll include the view contents for parsing within a catcher
// so we can avoid any WSOD errors. If an exception occurs we
// will throw it out to the exception handler.
try
{
eval('?>'.$generated);
}
// If we caught an exception, we'll silently flush the output
// buffer so that no partially rendered views get thrown out
// to the client and confuse the user with junk.
catch (\Exception $e)
{
ob_get_clean(); throw $e;
}
$content = ob_get_clean();
return $content;
}
For anyone still interested in this, they've added it to Laravel 9
use Illuminate\Support\Facades\Blade;
return Blade::render('Hello, {{ $name }}', ['name' => 'Julian Bashir']);
https://laravel.com/docs/9.x/blade#rendering-inline-blade-templates
I just stumbled upon the same requirement! For me, i had to fetch a blade template stored in DB & render it to send email notifications.
I did this in laravel 5.8 by kind-of Extending \Illuminate\View\View. So, basically i created the below class & named him StringBlade (I couldn't find a better name atm :/)
<?php
namespace App\Central\Libraries\Blade;
use Illuminate\Filesystem\Filesystem;
class StringBlade implements StringBladeContract
{
/**
* #var Filesystem
*/
protected $file;
/**
* #var \Illuminate\View\View|\Illuminate\Contracts\View\Factory
*/
protected $viewer;
/**
* StringBlade constructor.
*
* #param Filesystem $file
*/
public function __construct(Filesystem $file)
{
$this->file = $file;
$this->viewer = view();
}
/**
* Get Blade File path.
*
* #param $bladeString
* #return bool|string
*/
protected function getBlade($bladeString)
{
$bladePath = $this->generateBladePath();
$content = \Blade::compileString($bladeString);
return $this->file->put($bladePath, $content)
? $bladePath
: false;
}
/**
* Get the rendered HTML.
*
* #param $bladeString
* #param array $data
* #return bool|string
*/
public function render($bladeString, $data = [])
{
// Put the php version of blade String to *.php temp file & returns the temp file path
$bladePath = $this->getBlade($bladeString);
if (!$bladePath) {
return false;
}
// Render the php temp file & return the HTML content
$content = $this->viewer->file($bladePath, $data)->render();
// Delete the php temp file.
$this->file->delete($bladePath);
return $content;
}
/**
* Generate a blade file path.
*
* #return string
*/
protected function generateBladePath()
{
$cachePath = rtrim(config('cache.stores.file.path'), '/');
$tempFileName = sha1('string-blade' . microtime());
$directory = "{$cachePath}/string-blades";
if (!is_dir($directory)) {
mkdir($directory, 0777);
}
return "{$directory}/{$tempFileName}.php";
}
}
As you can already see from the above, below are the steps followed:
First converted the blade string to the php equivalent using \Blade::compileString($bladeString).
Now we have to store it to a physical file. For this storage, the frameworks cache directory is used - storage/framework/cache/data/string-blades/
Now we can ask \Illuminate\View\Factory native method 'file()' to compile & render this file.
Delete the temp file immediately (In my case i didn't need to keep the php equivalent file, Probably same for you too)
And Finally i created a facade in a composer auto-loaded file for easy usage like below:
<?php
if (! function_exists('string_blade')) {
/**
* Get StringBlade Instance or returns the HTML after rendering the blade string with the given data.
*
* #param string $html
* #param array $data
* #return StringBladeContract|bool|string
*/
function string_blade(string $html, $data = [])
{
return !empty($html)
? app(StringBladeContract::class)->render($html, $data)
: app(StringBladeContract::class);
}
}
Now i can call it from anywhere like below:
<?php
$html = string_blade('<span>My Name is {{ $name }}</span>', ['name' => 'Nikhil']);
// Outputs HTML
// <span>My Name is Nikhil</span>
Hope this helps someone or at-least maybe inspires someone to re-write in a better way.
Cheers!
I'm not using blade this way but I thought that the compile method accepts only a view as argument.
Maybe you're looking for:
Blade::compileString()
It's a old question. But I found a package which makes the job easier.
Laravel Blade String Compiler renders the blade templates from the string value. Check the documentation on how to install the package.
Here is an example:
$template = '<h1>{{ $name }}</h1>'; // string blade template
return view (['template' => $template], ['name' => 'John Doe']);
Note: The package is now updated to support till Laravel 6.
I know its pretty old thread, but today also requirement is same.
Following is the way I solved this on my Laravel 5.7 (but this will work with any laravel version greater than version 5), I used the knowledge gained from this thread and few other threads to get this working (will leave links to all threads at the end, if this help up-vote those too)
I added this to my helper.php (I used this technique to add helper to my project, but you can use this function directly as well)
if (! function_exists('inline_view')) {
/**
* Get the evaluated view contents for the given blade string.
*
* #param string $view
* #param array $data
* #param array $mergeData
* #return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
*/
function inline_view($view = null, $data = [], $mergeData = [])
{
/* Create a file with name as hash of the passed string */
$filename = hash('sha1', $view);
/* Putting it in storage/framework/views so that these files get cleared on `php artisan view:clear*/
$file_location = storage_path('framework/views/');
$filepath = storage_path('framework/views/'.$filename.'.blade.php');
/* Create file only if it doesn't exist */
if (!file_exists($filepath)) {
file_put_contents($filepath, $view);
}
/* Add storage/framework/views as a location from where view files can be picked, used in make function below */
view()->addLocation($file_location);
/* call the usual view helper to render the blade file created above */
return view($filename, $data, $mergeData);
}
}
Usage is exactly same as laravel's view() helper, only that now first parameter is the blade string
$view_string = '#if(strlen($name_html)>6)
<strong>{{ $name_html }}</strong>
#else
{{$name_html}}
#endif';
return inline_view($view_string)->with('name_html', $user->name);
return inline_view($view_string, ['name_html' => $user->name]);
References:
https://stackoverflow.com/a/31435824/4249775
https://stackoverflow.com/a/33594452/4249775
Laravel 9 :
use Illuminate\Support\Facades\Blade;
return Blade::render('Your Blade Content {{ $parameter1}}', ['parameter1' => 'Name']);