I have Symfony 6.2 application in which logged user may upload images to server. Because images are non public, I upload this images to /var/images directory.
In config/services.yaml I have
parameters:
card_directory: '%kernel.project_dir%/var/images'
and for upload service I use
App\Service\ImageUploader:
arguments:
$targetDirectory: '%card_directory%'
upload images is function.
But now I need serve this images to twig template for generating 4 column thumnail gallery. How I should generate path to this images in twig templates?
Finally I use this code for server private images with Controller for Card entity, which has image
<?php
namespace App\Controller;
use App\Entity\Card;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\Routing\Annotation\Route;
class ServePrivateImageController extends AbstractController
{
/**
* Returns a private image for card file for display.
*
* #Route("/serve-private-image/{id}", name="serve_private_image", methods="GET")
* #param Card $card
* #return BinaryFileResponse
*/
public function privateImageServe(Card $card): BinaryFileResponse
{
return $this->fileServe($card->getImage());
}
/**
* Returns a private file for display.
*
* #param string $path
* #return BinaryFileResponse
*/
private function fileServe(string $path): BinaryFileResponse
{
$absolutePath = $this->getParameter('card_directory') . '/' . $path;
return new BinaryFileResponse($absolutePath);
}
}
To answer your question, you would simply refer to your route within twig.
<div class="row">
{% for file in files %}
<div class="col-md-3">
<img src="{{ path('serve_private_image', {id: file.id}) }}" />
</div>
{% endfor %}
But as far as serving the resized images, LiipImageBundle does what you're looking for, and much more:
https://github.com/liip/LiipImagineBundle
Although it takes a few minutes to set up (because of the integration with flysystem), once it's working you can even keep your (secure) images on s3 and it handles all the downloading and local caching, rotating the resized issues, etc.
Related
Let's say I use Webpack, and builded a Css file properly at "build/theme/mail.css".
I wants to include the CONTENT of this File into my twig.
{% include "https://mysite.io/build/theme/mail.css" %} doesn't work saying it can't find the file ( but it exist).
I don't want that because im using an inliner, and absolutly need the #CONTENT in my twig.
Tried the File get Content, didn't works. Found a solution:
twig:
paths:
'%kernel.project_dir%/public': public
Add a twig path to public directory
and then use :
{% apply inline_css(source(theme_asset('#public/build/theme/email.css'))) %}
and add a webpackconfig to build the scss file into public/build
Thanks you all.
You could create custom twig extension and use file_get_contents.
Extension:
<?php
namespace App\Twig;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class GetPublicFileContentExtension extends AbstractExtension
{
/** #var string */
private $publicPath;
public function __construct(ParameterBagInterface $parameterBag)
{
$this->publicPath = $parameterBag->get('kernel.project_dir') . '/public/';
}
public function getFunctions(): array
{
return [
new TwigFunction('get_public_file_content', [$this, 'getPublicFileContent']),
];
}
public function getPublicFileContent(string $filepath)
{
return file_get_contents($this->publicPath . $filepath);
}
}
Twig:
{{ get_public_file_content('build/theme/mail.css') }}
I have a question about Symfony 2.
I would like to know how if there is a function implemented in Symfony 2 who return the content mime-type ?
Why mime-type cause trouble ? i have some file and i dont want everyone access to it then i made a methode who check if you have the right to access to this ressource.
chdir("Directory/".$nameoftheressource);
$file = file_get_contents($nameoftheressource);/**/
$namearray=explode(".", $nameoftheressource);
$extension=end($namearray);
$returnFile= new Response();
$returnFile->setContent($file);
if($extension == "css" )
{ $returnFile->headers->set('Content-Type', 'text/css');
return $returnFile;}
Thanks to you xabbuh this is near to work perfectly and as u said saved lot of time
now the code look like
EDIT
use Symfony\Component\HttpFoundation\BinaryFileResponse;
//some code
return new BinaryFileResponse('/Directory/'.$nameoftheressource);
But now it does display the css file but propose me to download it bt i would like to display it as a css normal css file
You can save a lot of code by using the BinaryFileResponse class which among other things automatically adds the right content type header.
It seems that you want to serve a protected CSS file. In this case, you can use the following code and protect the access to this controller using the Symfony Security system:
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
class DefaultController extends Controller
{
/**
* #Route("/css/app.css", name="css")
* #Security("has_role('ROLE_ADMIN')")
*/
public function renderCss()
{
$cssFilePath = $this->container->getParameter('kernel.root_dir').'/data/app.css';
$cssContent = file_get_contents($cssFilePath);
return Response($cssContent, 200, array('Content-Type' => 'text/css'));
}
}
I am try to use OneupUploaderBundle for uploading files. I read the documentation of this bundle many times but I did not manage to find any simple example of an entity for the file to be uploaded. My expectation is a class definition similar to the VichUploaderBundle:
<?php
namespace Minn\AdsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Validator\Constraints as Assert;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* #ORM\Entity
* #Vich\Uploadable
*/
class MotorsAdsFile {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* #Assert\File(
* maxSize="5M",
* mimeTypes={"image/png", "image/jpeg"}
* )
* #Vich\UploadableField(mapping="motors_files", fileNameProperty="filename")
* note: This is not a mapped field of entity metadata, just a simple property.
* #var File $file
*/
protected $file;
/**
* #ORM\Column(type="string", length=255, name="filename")
* #var string $filename
*/
protected $filename;
/**
* #ORM\Column(type="string", length=255, name="name")
* #var string $name
*/
protected $name;
// ...
}
Hope my question is clear...
I am really interested by this bundle since it supports jQuery.
Thanks,
There is no such thing as a predefined entity or ORM-handling. This has several reasons. This bundle can not foresee the projects need in terms of upload logic. If someone wants to store files to the database or not is entirely their own choice and should not be forced by a third party bundle. The thing the OneupUploaderBundle provides is a backend for the most common frontend uploaders there are.
Disclaimer: I've shorty copied and extended an answer that was already present in the GitHub issue tracker of this bundle. There you'll find quite a lot of insights on how and why this bundle is what it is now.
Given that you have already installed a working Symfony2 project, follow the installation instructions of the this bundle. I think step 1 and 2 should not be a problem, so lets hook directly to step 3, the configuration.
You said, you tried to integrate the jQuery File Uploader, so lets create a mapping for it.
Open the file app/config/config.yml and add the following lines to the end of it.
oneup_uploader:
mappings:
gallery:
frontend: blueimp
And of course, don't forget to add the following lines to app/config/routing.yml.
oneup_uploader:
resource: .
type: uploader
So much for the configuration. For the sake of simplicity we will alter the AcmeDemoBundle.
Open the file src/Acme/DemoBundle/Resources/views/Welcome/index.html.twig and delete everything between {% block content %} and {% endblock %}. We don't need this anymore.
Now insert the following:
<script type="text/javascript" src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="https://rawgithub.com/blueimp/jQuery-File-Upload/master/js/vendor/jquery.ui.widget.js"></script>
<script type="text/javascript" src="https://rawgithub.com/blueimp/jQuery-File-Upload/master/js/jquery.iframe-transport.js"></script>
<script type="text/javascript" src="https://rawgithub.com/blueimp/jQuery-File-Upload/master/js/jquery.fileupload.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#fileupload').fileupload({});
});
</script>
<input id="fileupload" type="file" name="files[]" data-url="{{ oneup_uploader_endpoint('gallery') }}" multiple />
Point your browser to the root directory of this application (app_dev.php). You'll see an input field just like expected and you can now upload some images (for example). The files will be stored in web/uploads/gallery every single one with a unique filename. Note that we used some CDNs to serve the JavaScript files needed for this.
At this point, you already have a working upload form. But besides uploading files to the uploads directory, it does nothing.
This is where the Next step section in the documentation comes in handy.
As I understand your question, you want to create an entity which stores a file path of a related file in it.
To do so, create your Entity class including all required fields.
<?php
namespace Minn\AdsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class MotorsAdsFile {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* #ORM\Column(type="string", length=255, name="filename")
* #var string $filename
*/
protected $filename;
// ...
}
Then create an EventListener as described in the documentation of this bundle.
<?php
namespace Acme\HelloBundle\EventListener;
use Oneup\UploaderBundle\Event\PostPersistEvent;
use Minn\AdsBundle\Entity\MotorsAdsFile;
class UploadListener
{
protected $manager;
public function __construct(EntityManager $manager)
{
$this->manager = $manager;
}
public function onUpload(PostPersistEvent $event)
{
$file = $event->getFile();
$object = new MotorsAdsFile();
$object->setFilename($file->getPathName());
$this->manager->persist($object);
$this->manager->flush();
}
}
And of course register your event listener.
<services>
<service id="acme_hello.upload_listener" class="Acme\HelloBundle\EventListener\UploadListener">
<argument type="service" id="doctrine.orm.entity_manager" />
<tag name="kernel.event_listener" event="oneup_uploader.post_persist" method="onUpload" />
</service>
</services>
At this point the EventListener will be called as soon as a new file was uploaded through the configured mapping.
It takes this file, creates a new object of MotorsAdsFile and stores the file path to the filename property, persists and flushes it to the underlying database.
As I can't predict your actual logic, this is the most basic example I can think of. You're of course free to do whatever is needed in the event listener. (In case you need to store this object to another entity or the like.)
You'll find a bunch of other topics in the Next steps section of the documentation. For example how you'd change the naming strategy of the uploaded files or how to enable chunked uploads in case you need to upload big files.
In my bundle I have a Resources/public/images/image.jpg file.
This image is accessible via http://localhost/bundles/mybundle/images/image.jpg
How can I get this /bundles/mybundle prefix from a controller?
I want to be able to generate the path to the public file without hardcoding /bundles/mybundle prefix.
I'd create a service which would do this
Create the service
The main responsibility of this class, is to get the default web path of any bundle for any resource.
As defined in assets:install command, relative paths for each bundle are expected to be /bundles/foobar/ for a given FooBarBundle
Acme\FooBundle\WebPathResolver
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
class WebPathResolver
{
/**
* Gets the prefix of the asset with the given bundle
*
* #param BundleInterface $bundle Bundle to fetch in
*
* #throws \InvalidArgumentException
* #return string Prefix
*/
public function getPrefix(BundleInterface $bundle)
{
if (!is_dir($bundle->getPath().'/Resources/public')) {
throw new \InvalidArgumentException(sprintf(
'Bundle %s does not have Resources/public folder',
$bundle->getName()
));
}
return sprintf(
'/bundles/%s',
preg_replace('/bundle$/', '', strtolower($bundle->getName()))
);
}
/**
* Get path
*
* #param BundleInterface $bundle Bundle to fetch in
* #param string $type Which folder to fetch in (image, css..)
* #param string $resource Resource (image1.png)
*
* #return string Resolved path
*/
public function getPath(BundleInterface $bundle, $type, $resource)
{
$prefix = $this->getPrefix($bundle);
return sprintf('%s/%s/%s', $prefix, $type, $resource);
}
}
Declare it in your service.yml
Nothing special, but an usual service
#AcmeFooBundle/Resources/config/services.yml
services:
acme_foo.webpath_resolver:
class: Acme\FooBundle\WebPathResolver
Usage
You can then use it in your controller like this
Acme\FooBundle\Controller\BarController::bazAction
$bundle = $this->get('http_kernel')->getBundle('AcmeFooBundle');
$path = $this->get('acme.webpath_resolver')->getPath($bundle, 'image', 'foo.png');
echo $path; // Outputs /bundles/acmefoo/image/foo.png
You use assets in templates, like this:
{% image '#AcmeFooBundle/Resources/public/images/example.jpg' %}
<img src="{{ asset_url }}" alt="Example" />
{% endimage %}
or directly in src:
<img src="{{ asset('#AcmeFooBundle/Resources/public/images/example.jpg') }}" alt="Example" />
In css files you need to use relative paths.
From controller you can get full path in the same way by:
$this->container->get('templating.helper.assets')->getUrl('#AcmeFooBundle/Resources/public/images/example.jpg');
You could use something like this, but is assumes the path is the lowercased bundle name.
$controller = $request->attributes->get('_controller');
$regexp = '/(.*)\\\Bundle\\\(.*)\\\Controller\\\(.*)Controller::(.*)Action/';
preg_match($regexp, $controller, $matches);
$imagePath = '/bundles/'. strtolower($matches[2]). '/images/image.jpg';
I've made it simpler (apologies for the complicated question which I've left at the bottom).
I want to have a twig template render another controller as a sub-part.
Here's the parent twig (Resources/views/Default/testRenderParent.html.twig):
<p>Look! I am your father!</p>
<p>But look, I am not your
{{ render(controller("SarelTestBundle:Default:testRenderChild")) }}</p>
Here's the child twig (Resources/views/Default/testRenderChild.html.twig):
KID
The controller (Controller/DefaultController.php):
<?php
namespace Sarel\Test\TestBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
/**
* #Template()
**/
class DefaultController extends Controller
{
/**
* #Route("/testRenderParent")
*/
public function testRenderParentAction()
{
return array();
}
/**
* #Route("/testRenderChild")
*/
public function testRenderChildAction() {
return array();
}
}
When you run this, with the following URL /testRenderChild you should get "KID" echoed.
When you run this, with the URL /testRenderParent (notice the "Parent" part), you should get echoed:
Look! I am your father!
But look, I am your KID
However, instead you get this echoed, and the error below it:
Look! I am your father!
But look, I am not your
Look! I am your father!
But look, I am not your
Look! I am your father!
But look, I am not your
Look! I am your father!
But look, I am not your
Look! I am your father!
But look, I am not your
Look! I am your father!
But look, I am not your
Look! I am your father!
But look, I am not your
Look! I am your father!
But look, I am not your
FatalErrorException: Error: Maximum function nesting level of '250' reached, aborting! in /Users/sarel/www/playpen/app/cache/dev/classes.php line 6134
Thus you can see, it's rendering "Look! I am your father! But look, I am your "... and then instead of rendering the child controller with it's twig template, it re-renders the parent, ending in an endless loop, which is arrested by the exception.
--- copy before I changed it ---
--- copy before I changed it ---
--- copy before I changed it ---
So I'm trying to embed a controller (and ultimately the template that the action renders) in my symfony2 project using the guidelines in enter link description here.
My Controller is defined below:
<?php
namespace OTS\CoreBundle\Controller;
/**
* #Template()
*/
class EmergencyContactsController extends Controller
{
public function addEmergencyContactAction(Request $request, $id) {
return array();
}
}
I have a twig template attached to that, which right now just have "hello world" in the form of a twig file called addEmergencyContact.html.twig.
When I go to the URL for this, which is something like localhost/my_route_to_it it works perfectly fine and I see "hello world" on the screen.
Now, according to this I merely have to put the following twig command in:
{{ render(controller('OTSCoreBundle:EmergencyContacts:addEmergencyContact', {'id': 15})) }}
When I do this and I load the holding-route it appears to be trying to load the main route, and then load the main route again inside the spot where I render the child-route. I know this because it's telling me there's variables missing - variables that I'm only using in the main twig file.
Yet, when I:
Don't have the {{ render... }} in there, it works fine - i.e. it's not other code that's giving me the exception of missing variable
Render only the child-route I get a nice "hello world" back - i.e. it's not the child twig or child controller that's buggy
Hmmm. The question is a bit confusing but:
A. Like all controller actions, your addEmergencyContact needs to return a response, not an array. So:
class EmergencyContactsController extends Controller
{
public function addEmergencyContactAction(Request $request, $id)
{
$tplData = array();
$tplData['id'] = $id;
return $this->render('CoreBundle::emergency_contact.html.twig', $tplData);
}
}
B. You need two template files. Something like:
class HelloWorldController extends Controller
{
public function helloWorldAction(Request $request, $id)
{
$tplData = array();
$tplData['id'] = $id;
return $this->render('CoreBundle::hello_world.html.twig', $tplData);
}
}
C. The render line then goes in the hello_world.html.twig file.
{{ render(controller('OTSCoreBundle:EmergencyContacts:addEmergencyContact', {'id': 15})) }}
D. Note that your emergency_contact template will only have access to values passed into it by the emergency contact action. It will not "inherit" the values that hello_world has.
So why is my code formatting messed up? Did not like numbered lists.
This turns out to be a bug in the way annotations work. When I change the controller from being globally #Template() enabled to moving that old-school back to each action individually, it works. Here's the updated controller, look at where the #Template() now sits:
<?php
namespace Sarel\Test\TestBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class DefaultController extends Controller
{
/**
* #Route("/testRenderParent")
* #Template()
*/
public function testRenderParentAction()
{
return array();
}
/**
* #Route("/testRenderChild")
* #Template()
*/
public function testRenderChildAction() {
return array();
}
}