Skip to content

Commit 5a1f127

Browse files
authored
Provide serialized way for easier 4.x upgrading. (#397)
* Provide serialized way. * Provide serialized way. * Add test case. * Cleanup.
1 parent e6a3924 commit 5a1f127

File tree

10 files changed

+143
-36
lines changed

10 files changed

+143
-36
lines changed

docs/sections/tasks/email.md

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -57,48 +57,42 @@ $data = [
5757
];
5858
```
5959

60-
You can also assemble a Mailer object manually and pass that along as settings directly:
60+
You can also assemble a Message object manually and pass that along as serialized settings array directly:
6161
```php
6262
$data = [
63-
'settings' => $mailerObject,
64-
'content' => $content,
63+
'class' => \Cake\Mailer\Message::class,
64+
'settings' => $messageObject->__serialize(),
65+
'serialized' => true,
6566
];
6667
```
67-
Deprecated: This is not recommended as it breaks as soon as the code changes.
68+
You can also use the convenience method `EmailTask::serialize()` here.
6869

69-
Or send reusable Emails via the Mailer object:
70+
It will not yet send emails here, only assemble them.
71+
The Email Queue task triggers the `deliver()` method.
72+
73+
Or even pass it as serialized string:
7074
```php
7175
$data = [
72-
'settings' => $mailerObject,
73-
'action' => 'myReusableEmail', // instead of content or headers
74-
'vars' => [$var1, $var2, $var3]
76+
'class' => \Cake\Mailer\Message::class,
77+
'settings' => serialize($messageObject),
78+
'serialized' => true,
7579
];
7680
```
7781
Deprecated: This is not recommended as it breaks as soon as the code changes.
7882

79-
Inside a controller you can for example do this for your mailers:
80-
```php
81-
$mailer = $this->getMailer('User');
82-
$mailer->viewBuilder()
83-
->setTemplate('register');
84-
$mailer->set...(...);
85-
86-
$this->loadModel('Queue.QueuedJobs')->createJob(
87-
'Queue.Email',
88-
['settings' => $mailer]
89-
);
90-
```
91-
Do not send your emails here, only assemble them. The Email Queue task triggers the `deliver()` method.
92-
93-
Note: In this case the object is stored serialized in the DB.
83+
Note: In this last case the object is stored PHP serialized in the DB.
9484
This can break when upgrading your core and the underlying class changes.
9585
So make sure to only upgrade your code when all jobs have been finished.
9686

87+
88+
## Using custom Email class
9789
If you are not using CakePHP core Email task:
9890

9991
The recommended way for Email task together with JsonSerializer is using the FQCN class string of the Message class:
10092

10193
```php
94+
use App\Mailer\Message; // or your custom FQCN
95+
10296
$data = [
10397
'class' => Message::class,
10498
'settings' => $settings,

phpstan.neon

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ parameters:
1313
- abort
1414
ignoreErrors:
1515
- '#Access to an undefined property Cake\\ORM\\BehaviorRegistry::\$Search#'
16-
- '#Parameter \#1 \$object.+ of function is\_a expects object, class-string\<Cake\\Mailer\\Message\>\|object given.#'
1716
- '#Negated boolean expression is always false.#'
1817
- '#Parameter \#1 \$.+ of function call_user_func_array expects .+, array.+ given.#'
1918

src/Command/JobCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public function execute(Arguments $args, ConsoleIo $io) {
8484
/** @var array<\Queue\Model\Entity\QueuedJob> $jobs */
8585
$jobs = $this->QueuedJobs->find()
8686
->select(['id', 'job_task', 'completed', 'attempts'])
87-
->orderDesc('id')
87+
->orderByDesc('id')
8888
->limit(20)->all()->toArray();
8989
if ($jobs) {
9090
$io->out('Last jobs are:');

src/Command/WorkerCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public function execute(Arguments $args, ConsoleIo $io) {
7878

7979
/** @var array<\Queue\Model\Entity\QueueProcess> $processes */
8080
$processes = $this->QueueProcesses->find()
81-
->orderDesc('modified')
81+
->orderByDesc('modified')
8282
->limit(10)->all()->toArray();
8383
if ($processes) {
8484
$io->out('Last jobs are:');

src/Model/Table/QueueProcessesTable.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ public function status(): array {
217217

218218
$results = $this->find()
219219
->where(['modified >' => $thresholdTime])
220-
->orderDesc('modified')
220+
->orderByDesc('modified')
221221
->enableHydration(false)
222222
->all()
223223
->toArray();

src/Model/Table/QueuedJobsTable.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ public function getFullStats(?string $jobTask = null): array {
412412
->select($fields)
413413
->where($conditions)
414414
->enableHydration(false)
415-
->orderDesc('id')
415+
->orderByDesc('id')
416416
->limit(static::STATS_LIMIT)
417417
->all()
418418
->toArray();

src/Queue/Task/EmailTask.php

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class EmailTask extends Task implements AddInterface, AddFromBackendInterface {
3232

3333
public Mailer $mailer;
3434

35+
public Message $message;
36+
3537
/**
3638
* List of default variables for Email class.
3739
*
@@ -92,6 +94,25 @@ public function add(?string $data): void {
9294
$this->io->out('Alternatively, you can pass the whole Mailer in `settings` key.');
9395
}
9496

97+
/**
98+
* @param \Cake\Mailer\Message $message
99+
*
100+
* @return array
101+
*/
102+
public static function serialize(Message $message): array {
103+
return $message->__serialize();
104+
}
105+
106+
/**
107+
* @param \Cake\Mailer\Message $object
108+
* @param array $config
109+
*
110+
* @return \Cake\Mailer\Message
111+
*/
112+
public static function unserialize(Message $object, array $config): Message {
113+
return $object->createFromArray($config);
114+
}
115+
95116
/**
96117
* @param array<string, mixed> $data The array passed to QueuedJobsTable::createJob()
97118
* @param int $jobId The id of the QueuedJob entity
@@ -108,13 +129,22 @@ public function run(array $data, int $jobId): void {
108129

109130
/** @var class-string<\Cake\Mailer\Message>|object|null $class */
110131
$class = $data['class'] ?? null;
111-
if ($class && (is_a($class, Message::class) || is_subclass_of($class, Message::class))) {
132+
/** @var \Cake\Mailer\Message|null $object */
133+
$object = $class ? new $class() : null;
134+
if ($class && $object && (is_subclass_of($class, Message::class) || is_a($object, Message::class))) {
112135
$settings = $data['settings'];
136+
$serialized = $data['serialized'] ?? false;
137+
138+
if ($serialized) {
139+
$this->message = is_array($settings) ? static::unserialize($object, $settings) : unserialize($settings);
140+
} else {
141+
/** @var class-string<\Cake\Mailer\Message> $class */
142+
$this->message = new $class($settings);
143+
}
113144

114-
$message = new $class($settings);
115145
try {
116146
$transport = TransportFactory::get($data['transport'] ?? 'default');
117-
$result = $transport->send($message);
147+
$result = $transport->send($this->message);
118148
} catch (Throwable $e) {
119149
$error = $e->getMessage();
120150
$error .= ' (line ' . $e->getLine() . ' in ' . $e->getFile() . ')' . PHP_EOL . $e->getTraceAsString();

tests/TestCase/Controller/Admin/QueueControllerTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public function testAddJob() {
102102
$this->assertResponseCode(302);
103103

104104
/** @var \Queue\Model\Entity\QueuedJob $job */
105-
$job = $jobsTable->find()->orderDesc('id')->firstOrFail();
105+
$job = $jobsTable->find()->orderByDesc('id')->firstOrFail();
106106
$this->assertSame('Queue.Example', $job->job_task);
107107
}
108108

tests/TestCase/Controller/Admin/QueuedJobsControllerTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ public function testImport() {
184184

185185
$queuedJobs = $this->getTableLocator()->get('Queue.QueuedJobs');
186186
/** @var \Queue\Model\Entity\QueuedJob $queuedJob */
187-
$queuedJob = $queuedJobs->find()->orderDesc('id')->firstOrFail();
187+
$queuedJob = $queuedJobs->find()->orderByDesc('id')->firstOrFail();
188188

189189
$this->assertSame('Webhook', $queuedJob->job_task);
190190
$this->assertSame('web-hook-102803234', $queuedJob->reference);

tests/TestCase/Queue/Task/EmailTaskTest.php

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@
77
use Cake\Core\Configure;
88
use Cake\Datasource\ConnectionManager;
99
use Cake\Mailer\Mailer;
10+
use Cake\Mailer\Message;
1011
use Cake\Mailer\Transport\DebugTransport;
1112
use Cake\Mailer\TransportFactory;
1213
use Cake\TestSuite\TestCase;
1314
use Queue\Console\Io;
1415
use Queue\Queue\Task\EmailTask;
16+
use Queue\Utility\JsonSerializer;
17+
use Queue\Utility\Serializer;
1518
use Shim\TestSuite\ConsoleOutput;
1619
use Shim\TestSuite\TestTrait;
1720
use Tools\Mailer\Message as MailerMessage;
@@ -21,7 +24,7 @@ class EmailTaskTest extends TestCase {
2124
use TestTrait;
2225

2326
/**
24-
* @var array
27+
* @var array<string>
2528
*/
2629
protected array $fixtures = [
2730
'plugin.Queue.QueuedJobs',
@@ -70,10 +73,91 @@ public function testAdd() {
7073
$queuedJobsTable = $this->getTableLocator()->get('Queue.QueuedJobs');
7174

7275
/** @var \Queue\Model\Entity\QueuedJob $queuedJob */
73-
$queuedJob = $queuedJobsTable->find()->orderDesc('id')->firstOrFail();
76+
$queuedJob = $queuedJobsTable->find()->orderByDesc('id')->firstOrFail();
7477
$this->assertSame('Queue.Email', $queuedJob->job_task);
7578
}
7679

80+
/**
81+
* @return void
82+
*/
83+
public function testAddMessageSerialized() {
84+
Configure::write('Queue.serializerClass', JsonSerializer::class);
85+
86+
$message = new Message();
87+
$message
88+
->setSubject('I haz Cake')
89+
->setEmailFormat(Message::MESSAGE_BOTH)
90+
->setBody([
91+
Message::MESSAGE_TEXT => 'text message',
92+
Message::MESSAGE_HTML => '<strong>html message</strong>',
93+
]);
94+
95+
$data = [
96+
'class' => Message::class,
97+
'settings' => $message->__serialize(),
98+
'serialized' => true,
99+
];
100+
101+
/** @var \Queue\Model\Table\QueuedJobsTable $queuedJobsTable */
102+
$queuedJobsTable = $this->getTableLocator()->get('Queue.QueuedJobs');
103+
$queuedJobsTable->createJob('Email', $data);
104+
105+
/** @var \Queue\Model\Entity\QueuedJob $queuedJob */
106+
$queuedJob = $queuedJobsTable->find()->orderByDesc('id')->firstOrFail();
107+
108+
$settings = Serializer::deserialize($queuedJob->data)['settings'];
109+
$message = (new Message())->createFromArray($settings);
110+
111+
$this->assertSame('I haz Cake', $message->getSubject());
112+
113+
$serialized = EmailTask::serialize($message);
114+
$message = EmailTask::unserialize(new Message(), $serialized);
115+
116+
$this->assertSame('I haz Cake', $message->getSubject());
117+
118+
$this->Task->run($data, 0);
119+
120+
$this->assertInstanceOf(Message::class, $this->Task->message);
121+
122+
Configure::delete('Queue.serializerClass');
123+
}
124+
125+
/**
126+
* @return void
127+
*/
128+
public function testAddMessagePhpSerialized() {
129+
$message = new Message();
130+
$message
131+
->setSubject('I haz Cake')
132+
->setEmailFormat(Message::MESSAGE_BOTH)
133+
->setBody([
134+
Message::MESSAGE_TEXT => 'text message',
135+
Message::MESSAGE_HTML => '<strong>html message</strong>',
136+
]);
137+
138+
$data = [
139+
'class' => Message::class,
140+
'settings' => serialize($message),
141+
'serialized' => true,
142+
];
143+
144+
/** @var \Queue\Model\Table\QueuedJobsTable $queuedJobsTable */
145+
$queuedJobsTable = $this->getTableLocator()->get('Queue.QueuedJobs');
146+
$queuedJobsTable->createJob('Email', $data);
147+
148+
/** @var \Queue\Model\Entity\QueuedJob $queuedJob */
149+
$queuedJob = $queuedJobsTable->find()->orderByDesc('id')->firstOrFail();
150+
151+
$settings = Serializer::deserialize($queuedJob->data)['settings'];
152+
$message = unserialize($settings);
153+
154+
$this->assertSame('I haz Cake', $message->getSubject());
155+
156+
$this->Task->run($data, 0);
157+
158+
$this->assertInstanceOf(Message::class, $this->Task->message);
159+
}
160+
77161
/**
78162
* @return void
79163
*/
@@ -145,7 +229,7 @@ public function testRunToolsEmailMessageClassString() {
145229
$queuedJobsTable = $this->getTableLocator()->get('Queue.QueuedJobs');
146230
$queuedJobsTable->createJob('Queue.Email', ['class' => $class, 'settings' => $settings]);
147231

148-
$queuedJob = $queuedJobsTable->find()->orderDesc('id')->firstOrFail();
232+
$queuedJob = $queuedJobsTable->find()->orderByDesc('id')->firstOrFail();
149233
$data = unserialize($queuedJob->data);
150234
/** @var \TestApp\Mailer\TestMailer $mailer */
151235
$class = $data['class'];

0 commit comments

Comments
 (0)