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