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

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)

Related

Laravel queue is not doing job in the background

I am trying to use laravel queue to send bulk emails. So far I have written down the logic and it works fine, but the problem is that when I wrote the logic in controller it takes a lot of time so I thought of using jobs but again the problem persists.
My Problem
My problem is that I am not able send the email in background even if I am using queue.
Controller
public function newsletter(Request $request)
{
//dd($request->all());
dispatch(new SendEmail($request));
Session::flash('message', 'Email Sent');
Session::flash('class', 'success');
return redirect()->route('news');
}
Jobs
public function handle(Request $request)
{
//
$data = array(
'message' => $request->message,
'subject' => $request->subject,
'file' => $request->file("file")
);
$teachingLevel = $request->highest_teaching_level;
$school = $request->school;
$province = $request->province;
$district = $request->district;
$subject = $request->subject;
if ($teachingLevel != "" && $school != "" && $province != "" && $district != "") {
$email = User::where('highest_teaching_level', $teachingLevel)->where('current_school_name', $school)->where('address','LIKE', '%'.$province.'%')->where('address','LIKE', '%'.$district.'%')->pluck('email');
}else{
$email = User::pluck('email');
}
foreach($email as $e)
{
Mail::to($e)->send(new NewsLetter($data, $subject));
}
}
The email is sent but it doesn't happen in the background. Maybe it has to do with the way I have passed $request variable in the handle() function.
Any help will be appreciated. Thanks!
Here is how I'm using Laravel jobs in my project:
SampleJob.php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Services\SampleService;
class SampleJob implements ShouldQueue {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
// if you omit this value, you'll be in trouble, believe me
public $tries = 1;
private $param;
public function __construct($param) {
$this->param = $param;
}
public function handle(SampleService $service) {
// do something with $this->param
// Also, notice that you can type hint classes in handle function declaration for DI
$service->doSomething($this->param);
}
}
SampleController.php
namespace App\Http\Controllers;
use App\Jobs\SampleJob;
class SampleController extends Controller {
public function sampleMethod(Request $request) {
$param = $request->input('param');
SampleJob::dispatch($param); // $param will be passed to SampleJob constructor
// ...
}
}
A few points worth to note are:
Read the comments in my code snippets
If you use db-based queue, migrate first with php artisan queue:table && php artisan migrate
Create jobs with artisan command: php artisan make:job Sample
Don't forget to run queue worker: php artisan queue:work. To make it run in background: sudo nohup php artisan queue:work > ./storage/logs/queue-worker.log &
Highly recommended: In deployment, use Supervisor to keep php artisan queue:work running in the background
If you manage to make the job work, all delayed (queued but not handled because of misconfiguration or not starting queue worker) works will be instantly executed.
Common pitfalls:
If you don't set $tries param, and somehow your job throws an error, laravel will try to retry that job again and again until your database is down :(
If http user and php user is different, and if you used Log in your job, nine out of ten times you face permission problem on storage directory. To avoid this problem, add 'permission' => '0666' to your log channel setting in config/logging.php
Queue worker does not detect your code change, thus restart queue worker by php artisan queue:restart after you make some change to code base.
My laravel version: 5.8
If you are going to work with “database” connection is neccesary to run the next migrations:
php artisan queue:table
php artisan migrate
Also have an Event and a Listener that implements the “ShouldQueue” interface, and by last register the event associated with the listener or listeners in your “providers/EventProvider.php” path and in “EventProvider.php” file add your event and listeners with the next notation as example:
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
];
Its important to understant few points related to following queue:restart command
php artisan queue:restart
For that to work, you need to be running the Queue Listener:
php artisan queue:listen
Ref: https://medium.com/ariel-mejia-dev/run-queues-in-the-background-on-development-in-laravel-8d697d812f29

DoctrineCacheBundle: Flush cache via SYmfony route

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.

Why Laravel keeps calling schedule() with every Artisan Command?

I have one table called dc_user_meta and I've created one artisan command and scheduled it in kernel.php. Just after cloning the repository, when I try to run PHP artisan migrate, I get this error.
[Illuminate\Database\QueryException]
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'database.dc_user_meta' doesn't exist (SQL: select * from `dc_user_met
a` where `meta_key` = usage_in_days)
Not only php artisan migrate but I am unable to run any artisan command at all! I don't know why PHP keeps calling schedule method every time I try to execute any artisan command.
Here in this case, What I can do to solve this error is put the cover over my logic in schedule method just like this.
if(Schema::hasTable('dc_user_meta')){
// Code here
}
But I don't think it's good in Long run. What's the right way to solve this error?
UPDATE:
I just tried covering call to command in kernel.php just like this but still no success!
if(Schema::hasTable('dc_user_meta')){
$schedule->command('usage:update')->daily();
}
UPDATE:
I got the solution. But I don't think it's the answer to the question. It solves my problem but I don't think it's standard Solution. I just covered by Command login just like this.
if(Schema::hasTable('dc_user_meta')){
// Command Logic
}
Any specific answer to why Laravel calls schedule() with every artisan command and how to solve the error in a standard way if something like this happens!
Technically the schedule method ist called via the constructor of Illuminate\Foundation\Console\Kernel ( This is the parent class of app\Console\Kernel.php)
So every time the console Kernel is instantiated, the schedule() method gets executed.
Let's see what gets executed in which scenario ( $schedule->call() can be replaced with $schedule->command() or $schedule->exec()):
protected function schedule(Schedule $schedule)
{
// everything that is inside the schedule function is executed everytime the console kernel is booted.
// gets exectuted every time
\App\User::where('foo', 1)->get();
$schedule->call(function() {
// gets executed for every call to php artisan schedule:run
\App\User::where('foo', 1)->get();
});
$schedule->call(function() {
// gets executed for every call to php artisan schedule:run
// IF the closure in the when() function is true;
\App\User::where('foo', 1)->get();
})->when(function() {
// if true is returned the scheduled command or closure is executed otherwise it is skipped
\Schema::hasColumn('user', 'foo');
});
}
But why HAS the schedule command to be exectuted with every command?
Well, obviously php artisan schedule:run is a console command itself. So it definitely needs information about scheduled commands.
Also other commands could need information about scheduled commands... For example if you want to write an artisan command list:scheduledTasks. This command would require that all scheduled commands have been added to the console schedule list.
Maybe there are several other (internal) arguments why the schedule function has to run everytime. ( I did not dig too deep in the source code... )
Nevertheless... information about scheduled commands could be useful to a variety of use cases.
Your error is with table dc_user_meta while your logic is of table user_meta you need to do Schema::hasTable('dc_user_meta')
I'm convinced that table dc_user_meta doesn't exist in database.
As I understand, yor have table "user_meta" not "dc_user_meta" but you have written the code to use table "dc_user_meta" hence there is an error saying "dc_user_meta" table not found.
If anyone still cares about this...
<?php
# This is your app/Console/Kernel.php
use ...;
class Kernel extends ConsoleKernel {
# Other stuff...
protected function schedule(Schedule $schedule) {
if( in_array('schedule:run', $_SERVER['argv']) ){
# Your scheduler commands here...
}
}
}

How to sleep PHP(Laravel 5.2) in background

I have created an artisan command that I want to run immediately after a method called. But the command contains a sleep(); command. I want to run that artisan command in background because the method need to return immediately response to user. My sample code is a below:
In route file
Route::get('test', function(){
Artisan::queue('close:bidding', ['applicationId' => 1]);
return 'called close:bidding';
});
In close:bidding command
public function handle()
{
$appId = $this->argument('applicationId');
//following code line is making the problem
sleep(1000 * 10);
//close bidding after certain close time
try{
Application::where('id', $appId)->update(['live_bidding_status' => 'closed']);
}catch (\PDOException $e){
$this->info($e->getMessage());//test purpose
}
$this->info($appId.": bid closed after 10 seconds of creation");
}
Problem
When I hit /test url the return string called close:bidding is being shown after 10 seconds browser loading, because there is a sleep(10 * 1000) inside the command.
What I want
I want to run the command in background. I mean when I hit the /test url it should immediately show up the called close:bidding but close:bidding command will be running in background. After 10 seconds it will update the application though the front-end user won't notice anything anything about it.
Partial Questions
Is it somehow related to multi threading?
Is it something that cannot be solve using PHP, should I think differently?
Is there any way to solve this problem even using Laravel queue?
Create a job
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class JobTest implements ShouldQueue
{
use InteractsWithQueue, Queueable, SerializesModels;
private $payload = [];
public function __construct($payload)
{
$this->payload = $payload;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
$appId = $this->payload('applicationId');
//following code line is making the problem
sleep(1000 * 10);
}
}
To push job background queue
Route::get('test', function(){
dispatch((new JobTest)->onQueue('queue_name'));
return 'called close:bidding';
});
At this state we have job and you dispath the job to the queue. But it not processed yet. We need queue listener or worker to process those job in background
php artisan queue:listen --queue=queue_name --timeout=0
OR
php artisan queue:work --queue=queue_name --timeout=0 //this will run forever
Note:
May be you can try supervisord,beanstakd for manage queue
For more info refer this
If you don't need all advantages of proper queue, which come at a price, it may be sufficient to use terminate middleware. It will do the job after response was sent to the browser.

using Cache in cronjobs with Laravel artisan

I am in need of some help. I want to use Laravel Cache class:
class Uploadcatalogfiles_Task
{
public function run($arguments)
{
error_reporting(0);
$uploadFlag = Cache::get('upload_is_working');
if ($uploadFlag == 1) {
echo date('Y-m-d H:i:s '),'BIT: CACHE SHOT!.',"\n";
die;
} else {
Cache::put('upload_is_working',1,60);
}
And $uploadFlag is always empty. Seems like Cache is always empty.
What am I doing wrong?
Thanks.
This sounds similar (tho not a duplicate) to this question.
Assuming your cron job is running an artisan command, you likely have an issue where you haven't defined which environment to use when using Laravel's worker.
The docs say to use:
php artisan command:your_command
But you likely want to define a specific environment, like any artisan command, to pull in the right database and cache connections:
php artisan command:your_command --env=production
So, your cron job might look something like this:
* * * * * /usr/bin/php /var/www/example.com/public/artisan command:your_command

Categories