Invalid upload directory for EasyAdmin ImageField once deployed on Heroku - symfony

I work on a Symfony 6.0.9 website with EasyAdmin to handle the administration panel.
I've got an entity User and an entity MediaObject that can be an image for example. The User has a property $profilePicture which is a MediaObject.
In EasyAdmin, I've got a UserCrudController like that :
class UserCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return User::class;
}
public function configureCrud(Crud $crud): Crud
{
...
}
public function configureFields(string $pageName): iterable
{
return [
...
FormField::addPanel('Pictures')->renderCollapsed(),
ImageField::new('profilePicture.filePath')
->setBasePath('/media')
->setUploadDir('public/media')
->setUploadedFileNamePattern('profilePicture.[extension]')
->setLabel('Profile picture')
->setColumns(4),
...
];
}
}
In my app/config/services.yaml file, I've set :
parameters:
app.path.media_object: /media
In my app/config/packages/vich_uploader.yaml file, I've set :
vich_uploader:
db_driver: orm
mappings:
media_object:
uri_prefix: '%app.path.media_object%'
upload_destination: '%kernel.project_dir%/public%app.path.media_object%'
When I'm in my localhost (so in dev env), everything works just fine.
When I build and deploy on Heroku, I works fine. But when I try to edit an user, I've got this error message in Heroku's logs :
[critical] Uncaught PHP Exception Symfony\Component\OptionsResolver\Exception\InvalidArgumentException: "An error has occurred resolving the options of the form "EasyCorp\Bundle\EasyAdminBundle\Form\Type\FileUploadType": Invalid upload directory "/app/public/media/" it does not exist or is not writable." at /app/vendor/symfony/form/ResolvedFormType.php line 93
I don't understand what I've done wrong. Thanks for helping me ! ;)

Related

Symfony 5 custom 404 page

I'm trying to create a custom 404 page for a Symfony 5 project that must:
Output a simple JSON-encoded string, like "Not found".
Said string must be read from a translation resource.
Have an additional Content-Type: application/json header.
There is a section in the Symfony docs, that attempts to explain how this can be achieved, but the information seems incomplete/incorrect, apparently being written for the 4.X version, even pointing to non-existent source files on GitHub.
I have managed to create an error controller, but it swallows all errors:
# config/packages/framework.yaml
framework:
error_controller: App\Controller\ErrorController::errorHandler
// src/Controller/ErrorController.php
class ErrorController extends AbstractController
{
public function errorHandler(TranslatorInterface $translator) : JsonResponse
{
return new JsonResponse($translator->trans('not_found'));
}
}
The problem is that this results in any error (including internal ones) returning a 404 page.
How can I make this controller/method handle only 404 errors and leave everything else to be handled as before by the framework itself?
For anyone else that is looking for a solution to a JSON 404 page for a Symfony application:
I was looking for a way to use a controller to handle specific error cases as it seemed the easiest option on the surface, but this does not seem to be possible, or at least I have not figured out how.
In the end, I reached a solution using events and event listeners:
Configuration:
# config/services.yaml
services:
...
# This listener handles only 404 errors in PROD mode
App\EventListener\ExceptionListener:
tags:
- { name: kernel.event_listener, event: kernel.exception }
Event listener:
// src/EventListener/ExceptionListener.php
class ExceptionListener {
public function onKernelException(ExceptionEvent $event) : void
{
if (
$_ENV['APP_ENV'] != 'prod'
|| !$event->isMasterRequest()
|| !$event->getThrowable() instanceof NotFoundHttpException
) {
return;
}
// Send a not found in JSON format
$event->setResponse(new JsonResponse($this->translator->trans('not_found')));
}
}

Command created issue

I have a command which is in production already and I suspect not to be working. The dev who worked on it is not there anymore. So I come here to find some help.
There is 2 things I don't understand.
1- The command name is inside the Controller folder... ApiController but it extends ContainerAwareCommand so I guess this is fine...
2- The command is not find, but might be related to the first point.
When I try: php bin/console app:commandTest
I've got his error in console:
There are no commands defined in the "app" namespace.
class ApiController extends ContainerAwareCommand
{
protected function configure () {
$this->setName('app:commandTest');
$this->setDescription("Some desc");
$this->setHelp("Some help");
}
public function execute(InputInterface $input, OutputInterface $output)
{ // whatever }
}
Peoples told me this code worked when the previous dev was working on it...but I can't see how actually. I hope you can see how to do it or how to make it work.
Thanks.
EDIT: What I tried to add to my services.yaml but it's not working
services:
app.command.api_controller:
class: AppBundle\Controller\ApiController
arguments: ["%command.default_name%"]
tags: - { name: console.command }
config.yaml
imports:
- { resource: services.yml }
But doing this there is an error
The file "/var/www/unitimmo/UniTimmo/app/config/services.yml" does not contain valid YAML

how to pass username to rollbar via monolog, symfony2

I am using rollbar.com to collect all details about exceptions in symfony2 app. However I don't understand how can I configure monolog so it would pass username and user id to rollbar.
I see that I can pass rollbar config as shown here and I am thinking person_fn is what I need. Still I don't know where to put this function (this should be in service because I need to check security token) and how to pass it to rollbar.
# config_prod.yml
rollbar:
type: rollbar
level: error
token: %rollbar_token%
config:
person_fn: getUserForRollbarRightAboutNowOrSomething
Found solution:
update monolog/monolog bundle to at least 1.17.0 version.
create ContextProcessor and update user information
#src/AppBundle/Monolog/RollbarContextProcessor
namespace AppBundle\Monolog;
use AppBundle\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class RollbarContextProcessor
{
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function processRecord($record)
{
if ($this->tokenStorage->getToken()) {
$user = $this->tokenStorage->getToken()->getUser();
if ($user instanceof User) {
$record['context']['payload']['person'] = [
'id' => $user->getId(),
'username' => $user->getUsername(),
'email' => $user->getEmail(),
];
}
}
return $record;
}
}
configure ContextProcessor as service with monolog.processor tag.
# app/config/config_prod.yml
services:
monolog.processor.rollbar_context:
class: AppBundle\Monolog\RollbarContextProcessor
arguments: [#security.token_storage]
tags:
- { name: monolog.processor, method: processRecord, handler: rollbar }
monolog:
handlers:
rollbar:
type: rollbar
level: error
token: %rollbar_token%
Your question has two parts:
Rollbar
person_fn is exactly what you need. You should be able to add a reference to the function by using a string (e.g.: "MyClass::static_function_reference" or "my_function_name").
Symfony
Disclaimer: I don't use or know much about Symfony.
This question has some excellent examples of how to get the current user in Symfony. (Punch line: in a controller you can call $this.getUser())
This question has a good example of how to inject the current user in a service. (Make a Twig Extension that depends on the SecurityContext or TokenStorage, use those dependencies to get a user objet).
Finally, there's the classic PHP move: as soon as you have a user add it to $_REQUEST. I'm not sure if Symfony co-opts this, but it'd be a valid way in a non-framework PHP application.

i got error "Value cannot be null.\r\nParameter name: serviceType"

I was following a tutorial , but after setting the project build path, I run the project and navigate to plugins. Then I got the following error.
An exception of type 'System.ArgumentNullException' occurred in Autofac.dll but was not handled in user code,
"Value cannot be null.\r\nParameter name: serviceType"
and i don't know that which method i should add and in which class?
Please help!
you must register the service you created in the plugin and also the repository for your entity in DependencyRegistrar.cs file so that run time Autofac can found them for example:
public class DependencyRegistrar : IDependencyRegistrar
{
public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder)
{
//data context
this.RegisterPluginDataContext<MYPLUGINObjectContext>(builder, "nop_object_context_misc_MYPLUGIN");
//override required repository with our custom context
builder.RegisterType<EfRepository<ENTITY>>()
.As<IRepository<ENTITY>>()
.WithParameter(ResolvedParameter.ForNamed<IDbContext>("nop_object_context_misc_MYPLUGIN"))
.InstancePerHttpRequest();
}
public int Order
{
get { return 0; }
}
}

Change database connection in Migration class in Symfony

In migration class depending on logic, I need to use different types of database connections. How in migration class to get new connection by connection name?
Currently in doctrine.yaml file I have connection names "default", "user", "admin" and "cron".
My migration class:
final class Version20190711123152 extends AbstractMigration
{
public function up(Schema $schema) : void
{
...
if($someCondition) {
$this->setConnection($wantedConnection) // how to set $wantedConnection for example on "admin" connection
}
}
/**
* #param Connection $connection
*/
public function setConnection(Connection $connection): void
{
$this->connection = $connection;
}
I am using Symfony 4.3
I don't know your exact use case, but I don't think the migrations should be conditional, i.e. you may end up with inconsistent databases across different environments.
Maybe consider storing migration files in separate directories and use different configuration and entity manager when running migrations.
# /config/migrations/default.yaml
name: "Default Migrations"
migrations_namespace: "App\Migrations\Default"
table_name: "doctrine_migration_versions"
migrations_directory: "src/Migrations/Default"
# /config/migrations/admin.yaml
name: "Admin Migrations"
migrations_namespace: "App\Migrations\Admin"
table_name: "doctrine_migration_versions"
migrations_directory: "src/Migrations/Admin"
Assuming you have configured two entity managers - one default and one i.e. with "admin" name you can run those migrations separately:
php bin/console doctrine:migrations:migrate --configuration=config/migrations/default.yaml
php bin/console doctrine:migrations:migrate --configuration=config/migrations/admin.yaml --em=admin

Resources