Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions src/Maker/MakeSchedule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<?php

/*
* This file is part of the Symfony MakerBundle package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bundle\MakerBundle\Maker;

use Symfony\Bundle\MakerBundle\ConsoleStyle;
use Symfony\Bundle\MakerBundle\DependencyBuilder;
use Symfony\Bundle\MakerBundle\FileManager;
use Symfony\Bundle\MakerBundle\Generator;
use Symfony\Bundle\MakerBundle\InputConfiguration;
use Symfony\Bundle\MakerBundle\Str;
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Process\Process;
use Symfony\Component\Scheduler\Attribute\AsSchedule;
use Symfony\Component\Scheduler\RecurringMessage;
use Symfony\Component\Scheduler\Schedule;
use Symfony\Component\Scheduler\ScheduleProviderInterface;
use Symfony\Contracts\Cache\CacheInterface;

/**
* @author Jesse Rushlow <[email protected]>
*
* @internal
*/
final class MakeSchedule extends AbstractMaker
{
private string $scheduleName;
private ?string $message = null;

public function __construct(
private FileManager $fileManager,
private Finder $finder = new Finder(),
) {
}

public static function getCommandName(): string
{
return 'make:schedule';
}

public static function getCommandDescription(): string
{
return 'Create a scheduler component';
}

public function configureCommand(Command $command, InputConfiguration $inputConfig): void
{
$command
->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeScheduler.txt'))
;
}

public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
{
if (!class_exists(AsSchedule::class)) {
$io->writeln('Running composer require symfony/scheduler');
$process = Process::fromShellCommandline('composer require symfony/scheduler');
$process->run();
$io->writeln('Scheduler successfully installed!');
}

// Loop over existing src/Message/* and ask which message the user would like to schedule
$availableMessages = ['Empty Schedule'];
$messageDir = $this->fileManager->getRootDirectory().'/src/Message';

if ($this->fileManager->fileExists($messageDir)) {
$finder = $this->finder->in($this->fileManager->getRootDirectory().'/src/Message');

foreach ($finder->files() as $file) {
$availableMessages[] = $file->getFilenameWithoutExtension();
}
}

$scheduleNameHint = 'MainSchedule';

// If the count is 1, no other messages were found - don't ask to create a message
if (1 !== \count($availableMessages)) {
$selectedMessage = $io->choice('Select which message', $availableMessages);

if ('Empty Schedule' !== $selectedMessage) {
$this->message = $selectedMessage;

// We don't want SomeMessageSchedule, so remove the "Message" suffix to give us SomeSchedule
$scheduleNameHint = sprintf('%sSchedule', Str::removeSuffix($selectedMessage, 'Message'));
}
}

// Ask the name of the new schedule
$this->scheduleName = $io->ask(question: 'What should we call the new schedule?', default: $scheduleNameHint);
}

public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
{
$scheduleClassDetails = $generator->createClassNameDetails(
$this->scheduleName,
'Scheduler\\',
);

$useStatements = new UseStatementGenerator([
AsSchedule::class,
RecurringMessage::class,
Schedule::class,
ScheduleProviderInterface::class,
CacheInterface::class,
]);

if (null !== $this->message) {
$useStatements->addUseStatement('App\\Message\\'.$this->message);
}

$generator->generateClass(
$scheduleClassDetails->getFullName(),
'scheduler/Schedule.tpl.php',
[
'use_statements' => $useStatements,
'has_custom_message' => null !== $this->message,
'message_class_name' => $this->message,
],
);

$generator->writeChanges();

$this->writeSuccessMessage($io);
}

public function configureDependencies(DependencyBuilder $dependencies): void
{
}
}
5 changes: 5 additions & 0 deletions src/Resources/config/makers.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@
<tag name="maker.command" />
</service>

<service id="maker.maker.make_schedule" class="Symfony\Bundle\MakerBundle\Maker\MakeSchedule">
<argument type="service" id="maker.file_manager" />
<tag name="maker.command" />
</service>

<service id="maker.maker.make_serializer_encoder" class="Symfony\Bundle\MakerBundle\Maker\MakeSerializerEncoder">
<tag name="maker.command" />
</service>
Expand Down
8 changes: 8 additions & 0 deletions src/Resources/help/MakeScheduler.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
The <info>%command.name%</info> command generates a schedule to automate repeated
tasks using Symfony's Scheduler Component.

If the Scheduler Component is not installed, <info>%command.name%</info> will
install it automatically using composer. You can of course do this manually by
running <info>composer require symfony/scheduler</info>.

<info>php %command.full_name%</info>
30 changes: 30 additions & 0 deletions src/Resources/skeleton/scheduler/Schedule.tpl.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?= "<?php\n" ?>

namespace <?= $namespace; ?>;

<?= $use_statements; ?>

#[AsSchedule]
final class <?= $class_name; ?> implements ScheduleProviderInterface
{
public function __construct(
private CacheInterface $cache,
) {
}

public function getSchedule(): Schedule
{
return (new Schedule())
->add(
<?php if ($has_custom_message): ?>
// @TODO - Modify the frequency to suite your needs
RecurringMessage::every('1 hour', new <?= $message_class_name; ?>()),
<?php else: ?>
// @TODO - Create a Message to schedule
// RecurringMessage::every('1 hour', new App\Message\Message()),
<?php endif ?>
)
->stateful($this->cache)
;
}
}
86 changes: 86 additions & 0 deletions tests/Maker/MakeScheduleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

/*
* This file is part of the Symfony MakerBundle package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bundle\MakerBundle\Tests\Maker;

use Symfony\Bundle\MakerBundle\Maker\MakeSchedule;
use Symfony\Bundle\MakerBundle\Test\MakerTestCase;
use Symfony\Bundle\MakerBundle\Test\MakerTestRunner;

class MakeScheduleTest extends MakerTestCase
{
protected function getMakerClass(): string
{
return MakeSchedule::class;
}

public function getTestDetails(): \Generator
{
yield 'it_generates_a_schedule' => [$this->createMakerTest()
->run(function (MakerTestRunner $runner) {
$output = $runner->runMaker([
'', // use default schedule name "MainSchedule"
]);

$this->assertStringContainsString('Success', $output);

self::assertFileEquals(
\dirname(__DIR__).'/fixtures/make-schedule/expected/DefaultScheduleEmpty.php',
$runner->getPath('src/Scheduler/MainSchedule.php')
);
}),
];

yield 'it_generates_a_schedule_select_empty' => [$this->createMakerTest()
->preRun(function (MakerTestRunner $runner) {
$runner->copy(
'make-schedule/standard_setup',
''
);
})
->run(function (MakerTestRunner $runner) {
$output = $runner->runMaker([
0, // Select "Empty Schedule"
'MySchedule', // Go with the default name "MainSchedule"
]);

$this->assertStringContainsString('Success', $output);

self::assertFileEquals(
\dirname(__DIR__).'/fixtures/make-schedule/expected/MyScheduleEmpty.php',
$runner->getPath('src/Scheduler/MySchedule.php')
);
}),
];

yield 'it_generates_a_schedule_select_existing_message' => [$this->createMakerTest()
->preRun(function (MakerTestRunner $runner) {
$runner->copy(
'make-schedule/standard_setup',
''
);
})
->run(function (MakerTestRunner $runner) {
$output = $runner->runMaker([
1, // Select "MyMessage" from choice
'', // Go with the default name "MessageFixtureSchedule"
]);

$this->assertStringContainsString('Success', $output);

self::assertFileEquals(
\dirname(__DIR__).'/fixtures/make-schedule/expected/MyScheduleWithMessage.php',
$runner->getPath('src/Scheduler/MessageFixtureSchedule.php')
);
}),
];
}
}
29 changes: 29 additions & 0 deletions tests/fixtures/make-schedule/expected/DefaultScheduleEmpty.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace App\Scheduler;

use Symfony\Component\Scheduler\Attribute\AsSchedule;
use Symfony\Component\Scheduler\RecurringMessage;
use Symfony\Component\Scheduler\Schedule;
use Symfony\Component\Scheduler\ScheduleProviderInterface;
use Symfony\Contracts\Cache\CacheInterface;

#[AsSchedule]
final class MainSchedule implements ScheduleProviderInterface
{
public function __construct(
private CacheInterface $cache,
) {
}

public function getSchedule(): Schedule
{
return (new Schedule())
->add(
// @TODO - Create a Message to schedule
// RecurringMessage::every('1 hour', new App\Message\Message()),
)
->stateful($this->cache)
;
}
}
29 changes: 29 additions & 0 deletions tests/fixtures/make-schedule/expected/MyScheduleEmpty.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace App\Scheduler;

use Symfony\Component\Scheduler\Attribute\AsSchedule;
use Symfony\Component\Scheduler\RecurringMessage;
use Symfony\Component\Scheduler\Schedule;
use Symfony\Component\Scheduler\ScheduleProviderInterface;
use Symfony\Contracts\Cache\CacheInterface;

#[AsSchedule]
final class MySchedule implements ScheduleProviderInterface
{
public function __construct(
private CacheInterface $cache,
) {
}

public function getSchedule(): Schedule
{
return (new Schedule())
->add(
// @TODO - Create a Message to schedule
// RecurringMessage::every('1 hour', new App\Message\Message()),
)
->stateful($this->cache)
;
}
}
30 changes: 30 additions & 0 deletions tests/fixtures/make-schedule/expected/MyScheduleWithMessage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace App\Scheduler;

use App\Message\MessageFixture;
use Symfony\Component\Scheduler\Attribute\AsSchedule;
use Symfony\Component\Scheduler\RecurringMessage;
use Symfony\Component\Scheduler\Schedule;
use Symfony\Component\Scheduler\ScheduleProviderInterface;
use Symfony\Contracts\Cache\CacheInterface;

#[AsSchedule]
final class MessageFixtureSchedule implements ScheduleProviderInterface
{
public function __construct(
private CacheInterface $cache,
) {
}

public function getSchedule(): Schedule
{
return (new Schedule())
->add(
// @TODO - Modify the frequency to suite your needs
RecurringMessage::every('1 hour', new MessageFixture()),
)
->stateful($this->cache)
;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace App\Message;

final class MessageFixture
{
public function __construct(
public string $message = 'Howdy!',
) {
}
}