DoctrineCacheBundle: Flush cache via SYmfony route - php

Over my Symfony project I use the DoctrineCacheBundle and I want when I visit http://example.com/api/cache/flush I want to uncache (flush) any cached key.
The sole reason is because I have applications that visit the url above in order to remove any cached result.
As far I searched the DoctrineCacheBundle uses a command in order to uncache the cached results (as you can see via php ./bin/console list doctrine:cache command):
Symfony 3.3.12 (kernel: app, env: dev, debug: true)
Usage:
command [options] [arguments]
Options:
-h, --help Display this help message
-q, --quiet Do not output any message
-V, --version Display this application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n, --no-interaction Do not ask any interactive question
-e, --env=ENV The environment name [default: "dev"]
--no-debug Switches off debug mode
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
Available commands for the "doctrine:cache" namespace:
doctrine:cache:clear Flush a given cache
doctrine:cache:contains Check if a cache entry exists
doctrine:cache:delete Delete a cache entry
doctrine:cache:flush [doctrine:cache:clear] Flush a given cache
doctrine:cache:stats Get stats on a given cache provider
But how can I do this programmatically?

The best way is to make your own Cache adapter by following one of theese 2 approaches:
Approach 1: Use dedicated manager for uncaching:
namespace AppBundle\CacheManagers;
use Doctrine\Common\Cache\FlushableCache;
class PurgeAllcachesManager
{
/**
* #var FlushableCache
*/
private $purgeCachingHandler=null;
public function __construct(FlushableCache $purgeCachingHandler)
{
$this->purgeCachingHandler=$purgeCachingHandler;
}
/**
* Method that does all the dirty job to uncache all the keys
*/
public function uncache()
{
$this->purgeCachingHandler->flushAll();
}
}
Approach2: Do as Doctrine does:
namespace AppBundle\CacheManagers;
use Doctrine\Common\Cache\Cache as CacheHandler;
class PurgeAllcachesManager
{
/**
* #var CacheHandler
*/
private $cacheHandler=null;
public function __construct(CacheHandler $cacheHandler)
{
$this->cacheHandler=$cacheHandler;
}
/**
* Method that does all the dirty job to uncache all the keys
* #throws Exception
*/
public function uncacheAllKeys()
{
if(!method_exists($this->purgeCachingHandler) ){
throw new Exception("You cannot empty the cache");
}
$this->purgeCachingHandler->flushAll();
}
//Yet another methods to handle the cache
}
Also have a look on this question for extra info on how to use it.

Related

Laravel 8 - Programmatically run migration and seeder when database has no tables

After connecting to database, I want to programmatically run migration and seeder once the project detects that the database doesn't have any tables.
I think what I should do is inject the code below somewhere, but I don't know what file I should edit.
if (!Schema::hasTable('users')) {
$init_met = ini_get('max_execution_time');
set_time_limit(300);
Artisan::call('migrate:fresh');
Artisan::call('db:seed');
set_time_limit($init_met);
}
Or, is there an alternative way to do this instead of injecting the code?
Thanks in advance.
i'd suggest you to look at composer scripts section - there are a lot of events that could be used as trigger for your code. for example post-autoload-dump event fired after composer dumpautoload which is fired in most common calls like install, update or by itself. the benefit of using composer events is that you don't need to check for existing tables on each request.
the most easy way to achieve this is to create custom artisan command
php artisan make:command PrepareEmptyDatabase
then in app\Console\Commands\PrepareEmptyDatabase.php
<?php
namespace App\Console\Commands;
use Exception;
use App\Http\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Artisan;
class PrepareEmptyDatabase extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'db:prepare-empty';
/**
* The console command description.
*
* #var string
*/
protected $description = 'check for users table and run migrations and seed if has not';
/**
* Execute the console command.
*
* #return int
*/
public function handle()
{
// don't forget to remove this if database user
// don't have access to dba tables
if (Schema::hasTable('users')) {
return Command::SUCCESS;
}
/*
// if your user doesn't have permission to access to dba tables
// you can simply try to do any request to users table
$needActions = false;
try {
User::first();
} catch (Exception $ex) {
$needActions = true;
}
if (!$needActions) {
return Command::SUCCESS;
}
*/
$init_met = ini_get('max_execution_time');
set_time_limit(300);
Artisan::call('migrate:fresh');
Artisan::call('db:seed');
set_time_limit($init_met);
return Command::SUCCESS;
}
}
and the last step is tie this command with composer event, so in composer.json
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"#php artisan package:discover",
"#php artisan db:prepare-empty"
]
},
now any time you install/update composer dependencies or just run composer dumpatoload application will run your custom command. or you can stick with any of provided in composer docs event on your taste
about running the same code after npm run dev
i'm not quite sure about place to search, guess its about webpack events, but your question tagged with laravel so i assume you're using laravel-mix and there are event hooks
quick googling says that nodejs can run bash scripts using nodejs child process
// webpack.mix.js
const exec = require('child_process')
// or import exec from 'child_process'
mix
.js('resources/assets/js/app.js', 'public/js')
// other configs
.after(() => {
exec.execSync('php artisan db:prepare-empty')
})
pay attention that this code will run on any mix even including npm run prod for example and i don't actually understand why do you need it in frontend build event (if only for testing purpose and even then its questionable)

How to set the locale on a Symfony CLI command?

I run a multi-language Symfony 4.4 website where the locale is part of the URL. So I have /it/, /fr/ and so on.
My routes look like this:
/**
* #Route({
* "it": "/it/azienda/",
* "es": "/es/empresa/"
* }, name="app_cms_about")
*/
Then I've a pre-controller event that set the correct locale:
public function onRouteRequest(ControllerEvent $event)
{
// ... checks and stuff ...
$event->getRequest()->setLocale($ln);
}
Everything works as expected via web, but now I need to manage this language difference in a CLI Command: basically it means that when I run $this->urlGenerator->generate('app_cms_about') from the CLI, I expect to get the locale-based URL.
The user must pass the locale to the CLI Command to as an argument:
protected function configure()
{
$this->addArgument(
"app_language",
InputArgument::REQUIRED,
'The site to run on: `it`, `es`, ...');
}
Now I just need to set this somehow. I'd love to it with an event, but of course there is no getRequest() on the CLI event to set the locale on.
How do I set the locale on a Symfony 4 CLI Command?
Setting the locale does not make a lot of sense for a console application. What has a locale is the user request, and there is no request during a command line run.
But you want seems to be to be able to get URLs with the appropriate locale:
Then, straight from the docs:
When a route is localized, Symfony uses by default the current request locale. Pass a different '_locale' value if you want to set the locale explicitly
E.g.:
$aboutUrlIt = $this->router->generate('app_cms_about', ['_locale' => 'it']);
You mention in comments the setting default_locale, and that since you can change this it means console applications "have a locale set". But you are reaching the wrong conclussion from this: this setting is to set a default locale in case where no locale is set in the request. Or, for example, when there is no request, as in a command line application.
You cannot change that setting during runtime, because that setting is part of the compiled container, which is compiled before the application is run.
Following Symfony calls with xdebug I was able to find the solution I was looking for. I built a listener as the one I'm using via web:
services:
App\EventListener\CommandStartListener:
tags:
- { name: kernel.event_listener, method: onCommandStart, event: console.command }
<?php
namespace App\EventListener;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Routing\RouterInterface;
class CommandStartListener
{
protected RouterInterface $router;
public function __construct(RouterInterface $router)
{
$this->router = $router;
}
public function onCommandStart(ConsoleCommandEvent $event) : ?string
{
// ... checks and stuff ...
try {
$lnParam = $event->getInput()->getArgument("app_language");
} catch( \Exception $ex) {
return null;
}
$this->router->getContext()->setParameter('_locale', $lnParam);
return $lnParam;
}
}
This makes any urlGenerator->generate() behave correctly.
hat tip to #yivi for pointing me into the right direction.

Why am I unable to catch the Unexpected Alert Exception?

I am using a try catch block to catch an exception and I am unable to catch it as it still says:
In Exception.php line 155:
unexpected alert open: {Alert text : The form is not complete and has not been submitted yet. There is 1 problem with your submission.}
(Session info: chrome=73.0.3683.75)
(Driver info: chromedriver=2.41.578700 (2f1ed5f9343c13f73144538f15c00b370eda6706),platform=Linux 4.15.0-38-generic x86_64)
My feature file:
<?php
use Behat\Behat\Hook\Scope\AfterStepScope;
use Behat\Behat\Tester\Exception\PendingException;
use Behat\Behat\Context\Context;
use Behat\MinkExtension\Context\MinkContext;
use WebDriver\Exception\UnexpectedAlertOpen;
/**
* Defines application features from the specific context.
*/
class FeatureContext extends MinkContext implements Context
{
/**
* Initializes context.
*
* Every scenario gets its own context instance.
* You can also pass arbitrary arguments to the
* context constructor through behat.yml.
*/
public function __construct()
{
}
/**
* #Given I fill in the email field with :email
*/
public function iFillInTheEmailFieldWith($email)
{
dump($email);
$this->visit('/471w2222');
$page = $this->getSession()->getPage();
$page->find('xpath', '//*[#id="tfa_1111"]')->setValue($email);
}
/**
* #When I submit the form
*/
public function iSubmitTheForm()
{
try {
$page = $this->getSession()->getPage();
$page->find('xpath', '//*[#id="submit_button"]')->click();
}
catch (UnexpectedAlertOpen $e){
dd($e->getMessage());
$this->getSession()->getDriver()->getWebDriverSession()->accept_alert();
}
}
}
The alert shows up :
$page->find('xpath', '//*[#id="submit_button"]')->click();
executes. But it is unable to catch it. Why?
As per the error message...
(Session info: chrome=73.0.3683.75)
(Driver info: chromedriver=2.41.578700 (2f1ed5f9343c13f73144538f15c00b370eda6706),platform=Linux 4.15.0-38-generic x86_64)
...the main issue is the incompatibility between the version of the binaries you are using as follows:
You are using chromedriver=2.41
Release Notes of chromedriver=2.41 clearly mentions the following :
Supports Chrome v67-69
You are using chrome=73.0
Release Notes of ChromeDriver v2.46 clearly mentions the following :
Supports Chrome v71-73
So there is a clear mismatch between the ChromeDriver v2.41 and the Chrome Browser v73.0
Solution
Upgrade ChromeDriver to current ChromeDriver v2.46 level.
Keep Chrome version between Chrome v73 levels. (as per ChromeDriver v2.45 release notes)
Clean your Project Workspace through your IDE and Rebuild your project with required dependencies only.
If your base Web Client version is too old, then uninstall it and install a recent GA and released version of Web Client.
Take a System Reboot.
Execute your #Test.
Always invoke driver.quit() within tearDown(){} method to close & destroy the WebDriver and Web Client instances gracefully.

Log Laravel with artisan output

I'm working with Laravel and I trying to do a log of some function output with this Log function:
Log::warning("some message")
But if I want to write in console and log file I need to write two times the same message with different function like this:
Log::warning("some message") //This work good for log file
dump("some message") //This work good for artisan console output
There is some function that allow me only use one of both?
You can use Laravel events to integrate this functionality without requiring any change to the way you currently log information.
Add a listener for the Illuminate\Log\Events\MessageLogged event, and within your listener output the log entry to the console if the request has come from the console -- using runningInConsole().
Register a new listener called MessageLoggedListener, e.g:
protected $listen = [
'Illuminate\Log\Events\MessageLogged' => [
'App\Listeners\MessageLoggedListener',
],
];
Generate your listener with php artisan event:generate
Add the event handler to your listener:
/**
* Handle the event.
*
* #param MessageLogged $event
* #return void
*/
public function handle(MessageLogged $event)
{
if (app()->runningInConsole()) {
$output = new ConsoleOutput();
$output->writeln("<error>{$event->message}</error>");
}
}
That's it! You're now ready to test the functionality. From a console command log a message, e.g:
public function handle()
{
Log::error('Hello world! This is an error.');
}
This is the output you'll see:
$ php artisan command
Hello world! This is an error.
And within your log file you'll see:
[2018-01-15 16:55:46] local.WARNING: Hello world! This is an error.
You can improve the functionality by adding different output styles, for example you may wish to use error for errors and info for info. You can read about styling your output here in the Symfony documentation.

yii console command custom help information

I created file commands/TestCommand.php in my yii-powered project:
class TestCommand extends CConsoleCommand
{
public function actionIndex()
{
echo "Hello World!\n";
}
}
And it's became visible via yiic:
Yii command runner (based on Yii v1.1.14)
Usage: yiic.php <command-name> [parameters...]
The following commands are available:
- message
- migrate
- shell
- test <<<
- webapp
To see individual command help, use the following:
yiic.php help <command-name>
If I am trying to get some help information about this console command:
php yiic.php help test
I see default text:
Usage: yiic.php test index
How can I write my TestCommand class which will show my help information? Is it a some public field or special method which return help text? I need something like that:
php yiic.php help webapp
Result like I need:
USAGE
yiic webapp <app-path> [<vcs>]
DESCRIPTION
This command generates an Yii Web Application at the specified location.
PARAMETERS
* app-path: required, the directory where the new application will be created.
If the directory does not exist, it will be created. After the application
is created, please make sure the directory can be accessed by Web users.
* vcs: optional, version control system you're going to use in the new project.
Application generator will create all needed files to the specified VCS
(such as .gitignore, .gitkeep, etc.). Possible values: git, hg. Do not
use this argument if you're going to create VCS files yourself.
You can override the default getHelp method to implements your help!
It must return a string that is the help text.
Provides the command description. This method may be overridden to return the actual command description.
Here is the default method:
public function getHelp()
{
$help='Usage: '.$this->getCommandRunner()->getScriptName().' '.$this->getName();
$options=$this->getOptionHelp();
if(empty($options))
return $help."\n";
if(count($options)===1)
return $help.' '.$options[0]."\n";
$help.=" <action>\nActions:\n";
foreach($options as $option)
$help.=' '.$option."\n";
return $help;
}
You can also override the default getOptionHelp method that is called in getHelp
Provides the command option help information. The default implementation will return all available actions together with their corresponding option information.
Source

Categories