Symfony 5 - Download files uploaded with vich - symfony

After add functionnality upload file or many files thanks the bundle "vich", i would download the file who uploaded. My files are saved into a upload folder created to the root folder.
I've this code but error saying "file not found"..
<a download class="btn btn-info btn-sm" href="{{'upload/' ~ document.image }}"><i class="fas fa-file-download"></i> {{ document.image }} </a>

To download a file when you use Vich uploader bundle, you have to create a function inside your controller class and call it from your template.
For example:
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Vich\UploaderBundle\Handler\DownloadHandler;
class MyController extends AbstractController
{
/**
* #Route(...)
*
* #param Document $document The document to download
* #param DownloadHandler $downloadHandler Handler
*
* #return Response
*/
public function downloadDocument(Document $document, DownloadHandler $downloadHandler): Response
{
return $downloadHandler->downloadObject($document, 'documentFile');
}
}
Form more information, see VichUploaderBundle serving_files_with_a_controller

Related

How to solve passing IDs in URL with Symfony

i'm making an app to make appointments with doctors, and in the template of the patient I need to use the id of the entity doctor in the path because I use it in the code of my controller, and I still can't get the Id of the entity doctor because when i do {{ doctor.id }} nothing appears on my screen, the content is empty.
As a solution I used the email of the doctor in the URL but it is quite ugly.
This is the code of the controller
/**
* #Route("/patient/ajouter-medecin-favoris/{id}", name="meet_my_doc_ajouter_medecin_favoris")
*/
public function ajouterMedecinFavoris(MedecinRepository $repoMedecin, ObjectManager $manager, $id)
{
$patient = $this->getUser();
$medecin = $repoMedecin->findOneById($id);
$patient->addMedecinsFavori($medecin);
$manager->persist($patient);
$manager->flush();
return $this->RedirectToRoute('accueil');
}
here's the code of the element I use to click on it to redirect on the controller previously
<fieldset>
<h4><i class="fa fa-star"></i> Dr. {{ medecin.nom }} {{ medecin.prenom }}</h4>
</fieldset>
I expected to be able to use the id of the doctor in the URL but I think when you're logged as an entity you can't get the ID (not the id used to log in) of other users and the content of the variable is empty. But I would know if there's a solution to use the ID because passing the email into the url isn't the solution I want, and I had the same problem on other piece of code where the of other entity is empty.
Here's the code of the controller that send the datas to the view
/**
* #Route("/patient/medecins-favoris", name="meet_my_doc_afficher_medecin_favoris")
*/
public function afficherMedecinFavoris()
{
$patient = $this->getUser();
$medecins = $this->getUser()->getMedecinsFavoris();
return $this->Render('meet_my_doc/afficherLesMedecinsFavoris.html.twig', ['medecins' => $medecins]);
}
try this
{{path('meet_my_doc_ajouter_medecin_favoris',{(id): medecin.id})}}
I would do something like this, first controller to receive Medicine Object like this, in this case you dont need to use $medecin = $repoMedecin->findOneById($id);
/**
* #Route("/patient/ajouter-medecin-favoris/{id}", name="meet_my_doc_ajouter_medecin_favoris")
*/
public function ajouterMedecinFavoris(MedecinRepository $repoMedecin, ObjectManager $manager, Medecin $medecin)
{
$patient = $this->getUser();
$patient->addMedecinsFavori($medecin);
$manager->persist($patient);
$manager->flush();
return $this->RedirectToRoute('accueil');
}
in twig in both cases you do something like this
{% for medecin in medecins %}
<fieldset>
<h4>
<i class="fa fa-star"></i>
Dr. {{ medecin.nom }} {{ medecin.prenom }}
</h4>
</fieldset>
{% endfor %}
If your code is how you've pasted it, then I think the issue lies in a typo - in the afficherMedecinFavoris() action you pass medicins to the view, and inside the view you try to access the id of medicin. So either remove the extra s in the afficherMedecinFavoris() or add that extra s to the path id parameter value.
Aside from that, I believe that this should work (under the condition that the identity field for Medecin entity is id):
/**
* #Route("/patient/ajouter-medecin-favoris/{id}", name="meet_my_doc_ajouter_medecin_favoris")
*/
public function ajouterMedecinFavoris(Medecin $medecin)
{
$patient = $this->getUser();
$patient->addMedecinsFavori($medecin);
$manager = $this->getDoctrine()->getManager();
$manager->persist($patient);
$manager->flush();
return $this->RedirectToRoute('accueil');
}
To know how the Medecin entity is available as an argument for the controller read this
I'm sorry to post the answer just now, but this how it worked for me : medecin inherit from User, so when I'm creating a medecin, it is creating also an User, both with the same Id, and if I want to use the medecin object's Id, I need to edit the getId() and replace the code by parent::getId(), this is the only way I've found to solve this problem.

On loading vich uploader image on Twig template showing File not uploadable exception

I´m facing a challenge when trying to show a image that was uploaded using vich uploader on twig template. An error message is going like this:
<img src="{{ vich_uploader_asset(manchete.arquivoDaImagem, 'arquivoDaImagem') }}">
But it´s throwing an error message like this:
An exception has been thrown during the rendering of a template ("The
class "Symfony\Component\HttpFoundation\File\File" is not uploadable.
If you use annotations to configure VichUploaderBundle, you probably
just forgot to add #Vich\Uploadable on top of your entity. If you
don't use annotations, check that the configuration files are in the
right place. In both cases, clearing the cache can also solve the
issue.").
My Manchete entity
/**
* #ORM\Entity(repositoryClass="App\Repository\MancheteRepository")
* #Vich\Uploadable
*/
class Manchete { ...
The file attribute is like this:
/**
* #Vich\UploadableField(mapping="manchete_images", fileNameProperty="caminhoDaImagem")
* #var File
*/
private $arquivoDaImagem;
Any idea what´s happening here?
I found I was passing:
<img src="{{ vich_uploader_asset(manchete.arquivoDaImagem, 'arquivoDaImagem') }}">
The correct approach is to pass:
<img src="{{ vich_uploader_asset(manchete, 'arquivoDaImagem') }}">
now it works.

Adding a search box to the homepage of a website built on Sylius

Could someone please explain how I can add a search box to the homepage of my website built on Sylius?
I have tried using the SyliusSearchBundle but that seems to be very out of data and the sample config settings throw errors.
I'd be grateful for any help.
Disclaimer
Steps below work at the time I'm writing. SyliusElasticSearchBundle plugin is in a heavy development state, some of the things crash now, but will be fixed for sure.
I've created a demo repository here: https://github.com/mheki/sylius-search-demo
You need running ElasticSearch and a SyliusElasticSearchBundle plugin.
Follow installation instructions from readme: https://github.com/Lakion/SyliusElasticSearchBundle
Just bear in mind to install sylius dev-master (circular dependency...)
Import bundle's config, routing, enable it in AppKernel.
At the moment I'm writing the bundle needed filter_sets config, otherwise it crashed
So start with the simple search by product name:
lakion_sylius_elastic_search:
filter_sets:
default:
filters:
name:
type: string
Populate elastic index with:
bin/console fos:elastic:pop
Override original routing for lakion_elastic_search_shop_product_index - use filter_set: for your channel code.
lakion_elastic_search_shop_product_index:
path: /products
methods: [GET]
defaults:
_controller: lakion_sylius_elastic_search.controller.search:filterAction
_sylius:
template: "#LakionSyliusElasticSearch/Product/index.html.twig"
resource_class: "%sylius.model.product.class%"
filter_set: default
requirements:
slug: .+
Original product index page was failing for me and I had to remove
{{ form_row(form.search) }}
from it. So copied #LakionSyliusElasticSearch/Product/index.html.twig into Resources directory:
Resources\LakionSyliusElasticSearchBundle\views\Product\index.html.twig and made that change.
Now the last thing is to create a form, for example copying the file _security.html.twig from SyliusShopBundle. Add something like this:
<div class="item">
<form action="{{ path('lakion_elastic_search_shop_product_index') }}" method="get">
<div class="ui icon input">
<input type="text" placeholder="{{ 'sylius.ui.search'|trans }}..." name="filter_set[name]" />
<button type="submit" class="ui button mini">
<i class="search icon"></i>
</button>
</div>
</form>
</div>
and here we go :)
Alternatively, if you don't want to use the SyliusElasticSearchBundle plugin, you can implement a simple search on your own (which search inside the product name, description and category name) :
Create a classic html search form, for example:
<form role="search" method="get" action="{{path('app_search_results') }}">
<input type="search" id="site-search" name="q"
placeholder="{{ 'walrus.search.input.placeholder'|trans }}"
aria-label="{{ 'walrus.search.aria.label'|trans }}">
<button>{{ 'walrus.search.aria.button'|trans }}</button>
</form>
Create a controller action for the route 'app_search_results' in order to get the search request sent by your search form :
<?php
namespace AppBundle\Controller;
use Sylius\Component\Channel\Context\ChannelContextInterface;
use Sylius\Component\Locale\Context\LocaleContextInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use \Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class SearchController extends Controller
{
/**
* #var LocaleContextInterface
*/
private $locale;
/**
* #var ChannelContextInterface
*/
private $channelContext;
/**
* #var RepositoryInterface
*/
private $productRepository;
public function __construct(
LocaleContextInterface $locale,
ChannelContextInterface $channelContext,
RepositoryInterface $productRepository
)
{
$this->locale = $locale;
$this->channelContext = $channelContext;
$this->productRepository = $productRepository;
}
/**
* #Route("/search", name="app_search_results")
*/
public function searchAction(Request $request) : Response
{
$searchTerm = $request->query->get('q');
$channel = $this->channelContext->getChannel();
$localeCode = $this->locale->getLocaleCode();
$products = $this->productRepository->findByTerm($channel, $localeCode, $searchTerm);
return $this->render(
'#App/search/searchListProducts.html.twig',
[
'products' => $products
]
);
}
}
Don't forget to manually wire the dependency injected through the constructor.
Override the product repository in order to implements the findByTerm() method :
<?php
namespace AppBundle\Repository;
use Sylius\Bundle\CoreBundle\Doctrine\ORM\ProductRepository as
BaseProductRepository;
use Sylius\Component\Core\Model\ChannelInterface;
class ProductRepository extends BaseProductRepository
{
public function findByTerm(ChannelInterface $channel, string $locale, $searchTerm): array
{
$qb = $this->createQueryBuilder('p')
->addSelect('translation')
// get the translated product for the product regarding the current locale
->innerJoin('p.translations', 'translation', 'WITH', 'translation.locale = :locale')
->orWhere('translation.name LIKE :searchTerm')
->orWhere('translation.description LIKE :searchTerm')
// get the taxons of the product
->innerJoin('p.productTaxons', 'productTaxon')
->innerJoin('productTaxon.taxon', 'taxon')
// get the translated taxon
->innerJoin('taxon.translations', 'taxonTranslation', 'WITH', 'taxonTranslation.locale = :locale')
->orWhere('taxonTranslation.name LIKE :searchTerm')
->andWhere(':channel MEMBER OF p.channels')
->andWhere('p.enabled = true')
->setParameter('searchTerm', '%'.$searchTerm.'%')
->setParameter('locale', $locale)
->setParameter('channel', $channel)
->getQuery();
return $qb->getResult();
}
}
You just need now to create the page rendering the list of product (the #App/search/searchListProducts.html.twig). And to tell Sylius to use your custom HomepageController instead of the original one. Same goes for your Product Repository. This can be done inside the services.yml file of you app folder :
services:
sylius.controller.shop.homepage:
public: true
class: AppBundle\Controller\Shop\HomepageController
arguments:
- '#templating'
- '#sylius.context.locale'
- '#sylius.repository.taxon'
sylius_product:
resources:
product:
classes:
repository: AppBundle\Repository\ProductRepository

let a html button call a Controller Action

How can I make my html Button
(like <button type="button" class="btn btn-warning btn-xs">delete</button>)
call a Controller Action like deleteAction($project)
from within my twig code?(or with java script)
In twig template
Delete
On your controller
/**
* #param User $entity
*
* #Route("/{id}/entity-remove", requirements={"id" = "\d+"}, name="delete_route_name")
* #return RedirectResponse
*
*/
public function deleteActionName(User $entity)
...
see http://symfony.com/doc/current/book/templating.html#linking-to-pages
Home

Loging on a cached page in an ESI component redirects on /_internal/secure/.../none.html

I've just setup varnish and i've done some tests on port 8080 before switching in production.
I've noticed that if i'm on a cached page :
/**
* #Cache(smaxage="10800")
* #Route("/{_locale}/", name="homepage2", requirements={"_locale" = "en|fr"})
* #Template()
*/
public function indexAction()
{
return array();
}
And I try to login (not using external services, but with normal login) via the component included via an ESI
{% render "GamerCertifiedHomeBundle:Home:login" with {}, { 'standalone': true } %}
It ends up redirecting me on a page with no style and no head with the url ...:8080/_internal/secure/MyBundleHomeBundle:Home:login/none.html
Step1 Screenshot / Step2 Screenshot
If I go back on the homepage, i'm logged in.
How can I avoid that please ?
EDIT :
Apache2.conf
Apache vhost
Varnish VCL
After analyzing the problem in the chat I found that _target_path for security successful redirect is generated in form in the next way:
<input type="hidden" name="_target_path" value="{{ app.request.uri }}" />
And since this part is rendered with standalone view - it has specific uri (with _internal prefix).
You can avoid this by applying changed logic for your app.request.uri injection.
Pass it to controller:
{% render yourAction with {'uri': app.request.uri}, {'standalone': true} %}
In your controller just pass it to your view
public function yourAction ($uri)
{
...
return array('uri' => $uri);
}
Use it in your template
<input type="hidden" name="_target_path" value="{{ uri }}" />
Enjoiy! ;)

Resources