Initial commit
This commit is contained in:
commit
897ca4c8bc
28
composer.json
Normal file
28
composer.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "slinicraftet204/notifyifavail",
|
||||||
|
"description": "benachrichtigt Kunden, sobald ein Produkt wieder verfügbar ist",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"type": "shopware-platform-plugin",
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Nils Gerhardt | SLINIcraftet204"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"shopware/core": "6.6.*",
|
||||||
|
"php": ">=8.0"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"NotifyIfAvail\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"shopware-plugin-class": "NotifyIfAvail\\NotifyIfAvail",
|
||||||
|
"label": {
|
||||||
|
"de-DE": "NotifyIfAvail (von Nils G.|SLINI)",
|
||||||
|
"en-GB": "NotifyIfAvail (by Nils G.|SLINI)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
config.xml
Normal file
7
config.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<config xmlns="https://shopware.com/schemas/plugin-config/1.0">
|
||||||
|
<card>
|
||||||
|
<title>Benachrichtigung Plugin Einstellungen</title>
|
||||||
|
<input-field name="emailSender" type="text" label="E-Mail Absender" defaultValue="shop@example.com"/>
|
||||||
|
<input-field name="emailSubject" type="text" label="E-Mail Betreff" defaultValue="Ihr gewünschter Artikel ist wieder verfügbar!"/>
|
||||||
|
</card>
|
||||||
|
</config>
|
11
plugin.xml
Normal file
11
plugin.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<plugin xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="https://developer.shopware.com/schemas/plugin.xsd">
|
||||||
|
<name>NotifyIfAvail</name>
|
||||||
|
<label>Benachrichtigungs Plugin</label>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<author>TTT-Games</author>
|
||||||
|
<copyright>TTT-Games</copyright>
|
||||||
|
<license>MIT</license>
|
||||||
|
<compatibility minVersion="6.5.0" />
|
||||||
|
</plugin>
|
46
src/Controller/NotificationController.php
Normal file
46
src/Controller/NotificationController.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NotifyIfAvail\Controller;
|
||||||
|
|
||||||
|
use Shopware\Core\Framework\Routing\Annotation\RouteScope;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Doctrine\DBAL\Connection;
|
||||||
|
use Shopware\Core\Framework\Uuid\Uuid;
|
||||||
|
use Symfony\Component\Mailer\MailerInterface;
|
||||||
|
use Symfony\Component\Mime\Email;
|
||||||
|
|
||||||
|
class NotificationController
|
||||||
|
{
|
||||||
|
private Connection $connection;
|
||||||
|
private MailerInterface $mailer;
|
||||||
|
|
||||||
|
public function __construct(Connection $connection, MailerInterface $mailer)
|
||||||
|
{
|
||||||
|
$this->connection = $connection;
|
||||||
|
$this->mailer = $mailer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/notification/subscribe", name="frontend.notification.subscribe", methods={"POST"})
|
||||||
|
*/
|
||||||
|
public function subscribe(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$email = $request->request->get('email');
|
||||||
|
$productId = $request->request->get('productId');
|
||||||
|
|
||||||
|
if (!$email || !$productId) {
|
||||||
|
return new JsonResponse(['message' => 'Invalid data'], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->connection->insert('notifyifavail_plugin_notification', [
|
||||||
|
'id' => Uuid::randomBytes(),
|
||||||
|
'product_id' => Uuid::fromHexToBytes($productId),
|
||||||
|
'email' => $email,
|
||||||
|
'created_at' => (new \DateTime())->format('Y-m-d H:i:s')
|
||||||
|
]);
|
||||||
|
|
||||||
|
return new JsonResponse(['message' => 'Successfully subscribed']);
|
||||||
|
}
|
||||||
|
}
|
45
src/Entity/Notification.php
Normal file
45
src/Entity/Notification.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NotifyIfAvail\Entity;
|
||||||
|
|
||||||
|
use Shopware\Core\Framework\DataAbstractionLayer\Entity;
|
||||||
|
use Shopware\Core\Framework\DataAbstractionLayer\EntityIdTrait;
|
||||||
|
|
||||||
|
class Notification extends Entity
|
||||||
|
{
|
||||||
|
use EntityIdTrait;
|
||||||
|
|
||||||
|
protected string $email;
|
||||||
|
protected string $productId;
|
||||||
|
protected ?\DateTimeInterface $createdAt;
|
||||||
|
|
||||||
|
public function getEmail(): string
|
||||||
|
{
|
||||||
|
return $this->email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setEmail(string $email): void
|
||||||
|
{
|
||||||
|
$this->email = $email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProductId(): string
|
||||||
|
{
|
||||||
|
return $this->productId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setProductId(string $productId): void
|
||||||
|
{
|
||||||
|
$this->productId = $productId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCreatedAt(): ?\DateTimeInterface
|
||||||
|
{
|
||||||
|
return $this->createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCreatedAt(\DateTimeInterface $createdAt): void
|
||||||
|
{
|
||||||
|
$this->createdAt = $createdAt;
|
||||||
|
}
|
||||||
|
}
|
22
src/Entity/NotificationCollection.php
Normal file
22
src/Entity/NotificationCollection.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NotifyIfAvail\Entity;
|
||||||
|
|
||||||
|
use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method void add(Notification $entity)
|
||||||
|
* @method void set(string $key, Notification $entity)
|
||||||
|
* @method Notification[] getIterator()
|
||||||
|
* @method Notification[] getElements()
|
||||||
|
* @method Notification|null get(string $key)
|
||||||
|
* @method Notification|null first()
|
||||||
|
* @method Notification|null last()
|
||||||
|
*/
|
||||||
|
class NotificationCollection extends EntityCollection
|
||||||
|
{
|
||||||
|
protected function getExpectedClass(): string
|
||||||
|
{
|
||||||
|
return Notification::class;
|
||||||
|
}
|
||||||
|
}
|
42
src/Entity/NotificationDefinition.php
Normal file
42
src/Entity/NotificationDefinition.php
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NotifyIfAvail\Entity;
|
||||||
|
|
||||||
|
use Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition;
|
||||||
|
use Shopware\Core\Framework\DataAbstractionLayer\Field\EmailField;
|
||||||
|
use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\PrimaryKey;
|
||||||
|
use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\Required;
|
||||||
|
use Shopware\Core\Framework\DataAbstractionLayer\Field\IdField;
|
||||||
|
use Shopware\Core\Framework\DataAbstractionLayer\Field\StringField;
|
||||||
|
use Shopware\Core\Framework\DataAbstractionLayer\Field\CreatedAtField;
|
||||||
|
use Shopware\Core\Framework\DataAbstractionLayer\FieldCollection;
|
||||||
|
|
||||||
|
class NotificationDefinition extends EntityDefinition
|
||||||
|
{
|
||||||
|
public const ENTITY_NAME = 'notifyifavail_plugin_notification';
|
||||||
|
|
||||||
|
public function getEntityName(): string
|
||||||
|
{
|
||||||
|
return self::ENTITY_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCollectionClass(): string
|
||||||
|
{
|
||||||
|
return NotificationCollection::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEntityClass(): string
|
||||||
|
{
|
||||||
|
return Notification::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function defineFields(): FieldCollection
|
||||||
|
{
|
||||||
|
return new FieldCollection([
|
||||||
|
(new IdField('id', 'id'))->addFlags(new PrimaryKey(), new Required()),
|
||||||
|
(new EmailField('email', 'email'))->addFlags(new Required()),
|
||||||
|
(new StringField('product_id', 'productId'))->addFlags(new Required()),
|
||||||
|
new CreatedAtField()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
43
src/Entity/NotificationRepository.php
Normal file
43
src/Entity/NotificationRepository.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NotifyIfAvail\Entity;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Connection;
|
||||||
|
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
|
||||||
|
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
|
||||||
|
use Shopware\Core\Framework\Uuid\Uuid;
|
||||||
|
|
||||||
|
class NotificationRepository
|
||||||
|
{
|
||||||
|
private Connection $connection;
|
||||||
|
|
||||||
|
public function __construct(Connection $connection)
|
||||||
|
{
|
||||||
|
$this->connection = $connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saveNotification(string $email, string $productId): void
|
||||||
|
{
|
||||||
|
$this->connection->insert('notifyifavail_plugin_notification', [
|
||||||
|
'id' => Uuid::randomBytes(),
|
||||||
|
'email' => $email,
|
||||||
|
'product_id' => Uuid::fromHexToBytes($productId),
|
||||||
|
'created_at' => (new \DateTime())->format('Y-m-d H:i:s'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteNotification(string $productId): void
|
||||||
|
{
|
||||||
|
$this->connection->delete('notifyifavail_plugin_notification', [
|
||||||
|
'product_id' => Uuid::fromHexToBytes($productId),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNotificationsForProduct(string $productId): array
|
||||||
|
{
|
||||||
|
return $this->connection->fetchFirstColumn(
|
||||||
|
"SELECT email FROM notifyifavail_plugin_notification WHERE product_id = :productId",
|
||||||
|
['productId' => Uuid::fromHexToBytes($productId)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
32
src/Migration/Migration20250305AddNotificationTable.php
Normal file
32
src/Migration/Migration20250305AddNotificationTable.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NotifyIfAvail\Migration;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Connection;
|
||||||
|
use Shopware\Core\Framework\Migration\MigrationStep;
|
||||||
|
|
||||||
|
class Migration20250305AddNotificationTable extends MigrationStep
|
||||||
|
{
|
||||||
|
public function getCreationTimestamp(): int
|
||||||
|
{
|
||||||
|
return 20250305000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(Connection $connection): void
|
||||||
|
{
|
||||||
|
$connection->executeStatement("
|
||||||
|
CREATE TABLE IF NOT EXISTS `notifyifavail_plugin_notification` (
|
||||||
|
`id` BINARY(16) NOT NULL,
|
||||||
|
`product_id` BINARY(16) NOT NULL,
|
||||||
|
`email` VARCHAR(255) NOT NULL,
|
||||||
|
`created_at` DATETIME(3) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateDestructive(Connection $connection): void
|
||||||
|
{
|
||||||
|
// Not needed
|
||||||
|
}
|
||||||
|
}
|
4
src/Resources/config/migration.xml
Normal file
4
src/Resources/config/migration.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
<migration xmlns="https://shopware.com/schemas/migration/1.0">
|
||||||
|
<update>NotifyIfAvail\Migration\Migration20250305AddNotificationTable</update>
|
||||||
|
</migration>
|
23
src/Resources/config/services.xml
Normal file
23
src/Resources/config/services.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
<container xmlns="http://symfony.com/schema/dic/services"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://symfony.com/schema/dic/services
|
||||||
|
http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||||
|
|
||||||
|
<services>
|
||||||
|
<service id="NotifyIfAvail\Storefront\Subscriber\ProductSubscriber">
|
||||||
|
<tag name="kernel.event_subscriber"/>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<service id="NotifyIfAvail\Controller\NotificationController">
|
||||||
|
<argument type="service" id="doctrine.dbal.default_connection"/>
|
||||||
|
<argument type="service" id="mailer.mailer"/>
|
||||||
|
<tag name="controller.service_arguments"/>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<service id="NotifyIfAvail\Service\NotificationService">
|
||||||
|
<argument type="service" id="doctrine.dbal.default_connection"/>
|
||||||
|
<argument type="service" id="mailer.mailer"/>
|
||||||
|
</service>
|
||||||
|
</services>
|
||||||
|
</container>
|
13
src/Resources/email-templates/notification_email.html.twig
Normal file
13
src/Resources/email-templates/notification_email.html.twig
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{SALUTATION} {CUSTOMER_NAME},
|
||||||
|
|
||||||
|
Sie haben sich für eine Benachrichtigung angemeldet, sobald der folgende Artikel wieder verfügbar ist:
|
||||||
|
|
||||||
|
**{PRODUCT_NAME}**
|
||||||
|
Jetzt wieder auf Lager! Sie können den Artikel hier aufrufen und bestellen:
|
||||||
|
|
||||||
|
[Zum Artikel]({PRODUCT_URL})
|
||||||
|
|
||||||
|
Vielen Dank für Ihr Interesse an unseren Produkten!
|
||||||
|
|
||||||
|
Beste Grüße,
|
||||||
|
Ihr {SHOP_NAME}-Team
|
7
src/Resources/snippets/de-DE.json
Normal file
7
src/Resources/snippets/de-DE.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"NotifyIfAvail": {
|
||||||
|
"notify_me": "Benachrichtigen, wenn verfügbar",
|
||||||
|
"email_placeholder": "Geben Sie Ihre E-Mail-Adresse ein",
|
||||||
|
"success_message": "Sie werden benachrichtigt, sobald der Artikel verfügbar ist."
|
||||||
|
}
|
||||||
|
}
|
7
src/Resources/snippets/en-EN.json
Normal file
7
src/Resources/snippets/en-EN.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"NotifyIfAvail": {
|
||||||
|
"notify_me": "Notify me when available",
|
||||||
|
"email_placeholder": "Enter your email address",
|
||||||
|
"success_message": "You will be notified when the item is available."
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
{% if product.extensions.notification is defined %}
|
||||||
|
<div id="notification-container">
|
||||||
|
<button class="btn btn-primary" id="notify-me-button">
|
||||||
|
{{ "NotifyIfAvail.notify_me"|trans }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div id="notify-me-form" style="display: none;">
|
||||||
|
<input type="email" id="notify-me-email" class="form-control"
|
||||||
|
placeholder="{{ 'NotifyIfAvail.email_placeholder'|trans }}" required>
|
||||||
|
<button class="btn btn-success" id="submit-notify">
|
||||||
|
{{ "NotifyIfAvail.notify_me"|trans }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById('notify-me-button').addEventListener('click', function() {
|
||||||
|
document.getElementById('notify-me-form').style.display = 'block';
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('submit-notify').addEventListener('click', function() {
|
||||||
|
let email = document.getElementById('notify-me-email').value;
|
||||||
|
let productId = '{{ product.id }}';
|
||||||
|
|
||||||
|
fetch('/notification/subscribe', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||||
|
body: `email=${encodeURIComponent(email)}&productId=${encodeURIComponent(productId)}`
|
||||||
|
}).then(response => response.json()).then(data => {
|
||||||
|
alert(data.message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
48
src/Service/NotificationService.php
Normal file
48
src/Service/NotificationService.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NotifyIfAvail\Service;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Connection;
|
||||||
|
use Symfony\Component\Mailer\MailerInterface;
|
||||||
|
use Symfony\Component\Mime\Email;
|
||||||
|
use Shopware\Core\Framework\Uuid\Uuid;
|
||||||
|
|
||||||
|
class NotificationService
|
||||||
|
{
|
||||||
|
private Connection $connection;
|
||||||
|
private MailerInterface $mailer;
|
||||||
|
|
||||||
|
public function __construct(Connection $connection, MailerInterface $mailer)
|
||||||
|
{
|
||||||
|
$this->connection = $connection;
|
||||||
|
$this->mailer = $mailer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function notifyCustomers(string $productId, string $productName, string $productUrl, string $shopName): void
|
||||||
|
{
|
||||||
|
$sql = "SELECT email FROM notifyifavail_plugin_notification WHERE product_id = :productId";
|
||||||
|
$emails = $this->connection->fetchFirstColumn($sql, ['productId' => Uuid::fromHexToBytes($productId)]);
|
||||||
|
|
||||||
|
foreach ($emails as $email) {
|
||||||
|
$message = (new Email())
|
||||||
|
->from('shop@example.com')
|
||||||
|
->to($email)
|
||||||
|
->subject('Ihr gewünschter Artikel ist wieder verfügbar!')
|
||||||
|
->html("
|
||||||
|
<p>Hallo,</p>
|
||||||
|
<p>Sie haben sich für eine Benachrichtigung angemeldet, sobald der folgende Artikel wieder verfügbar ist:</p>
|
||||||
|
<p><strong>{$productName}</strong></p>
|
||||||
|
<p><a href='{$productUrl}'>Jetzt bestellen</a></p>
|
||||||
|
<p>Vielen Dank für Ihr Interesse an unseren Produkten!</p>
|
||||||
|
<p>Beste Grüße,<br>{$shopName}-Team</p>
|
||||||
|
");
|
||||||
|
|
||||||
|
$this->mailer->send($message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nach Versand aus der Datenbank löschen
|
||||||
|
$this->connection->executeStatement("DELETE FROM notifyifavail_plugin_notification WHERE product_id = :productId", [
|
||||||
|
'productId' => Uuid::fromHexToBytes($productId)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
25
src/Storefront/Subscriber/ProductSubscriber.php
Normal file
25
src/Storefront/Subscriber/ProductSubscriber.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NotifyIfAvail\Storefront\Subscriber;
|
||||||
|
|
||||||
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
|
use Shopware\Storefront\Page\Product\ProductPageLoadedEvent;
|
||||||
|
|
||||||
|
class ProductSubscriber implements EventSubscriberInterface
|
||||||
|
{
|
||||||
|
public static function getSubscribedEvents(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
ProductPageLoadedEvent::class => 'onProductPageLoaded'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onProductPageLoaded(ProductPageLoadedEvent $event): void
|
||||||
|
{
|
||||||
|
$product = $event->getPage()->getProduct();
|
||||||
|
|
||||||
|
if ($product->getAvailableStock() <= 0) {
|
||||||
|
$product->addExtension('notification', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user