Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix links, add database info, add grammar and language #1148

Merged
merged 1 commit into from
Oct 24, 2023
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
8 changes: 1 addition & 7 deletions guides/plugins/apps/app-sdks/symfony-bundle/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,7 @@ The registration also dispatches events to react to the different lifecycle even

### 4. Connecting Doctrine to a Database

The App Bundle brings, by default, a basic Shop entity to store the shop information.
You can extend this entity to store more information about your app if needed.

Doctrine is, by default, configured to use PostgreSQL. If you want to use MySQL, change the `DATABASE_URL` environment variable in your `.env` file.
For development, you can also use SQLite by setting the `DATABASE_URL` to `sqlite:///%kernel.project_dir%/var/app.db`.

After choosing your database engine, create your first migration using `./bin/console make:migration` (Requires Migration Bundle `composer req migrations`) and apply it with the command: `bin/console doctrine:migrations:migrate`.
<<< @/docs/snippets/guide/app_database_setup.md

### 5. Implement first ActionButtons, Webhooks, Payment

Expand Down
139 changes: 124 additions & 15 deletions guides/plugins/apps/starter/product-translator.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ nav:

# Starter Guide - Read and Write Data

This guide will show you how to set up an app server with our [app template](https://github.com/shopware/AppTemplate).
This guide will show you how to set up an app server with our [app bundle](https://github.com/shopware/app-bundle-symfony).
You will learn how to read and write data to the Shopware Admin API using an example of fetching dynamic translations for products when they are updated.

## Prerequisites
Expand All @@ -19,7 +19,7 @@ You will learn how to read and write data to the Shopware Admin API using an exa

## Setting up the app template

First we need to create a new Symfony project using Symfony-CLI
First, we need to create a new Symfony project using Symfony-CLI

```sh
symfony new translator-app
Expand All @@ -33,7 +33,26 @@ Now we need to install the Shopware App Bundle with Composer:
composer require shopware/app-bundle
```

Modify the `SHOPWARE_APP_NAME` and `SHOPWARE_APP_SECRET` in the env to your app name`./.env` to ensure the app can be installed in a store later.
::: warning
Make sure that you agree to second interaction of the bundle recipe. It will add your routing, register the bundle, and more. If you do not agree to it, you will have to
create those manually (check files [here](https://github.com/symfony/recipes-contrib/tree/main/shopware/app-bundle/1.0))
:::

```shell
- WARNING shopware/app-bundle (>=1.0): From github.com/symfony/recipes-contrib:main
The recipe for this package comes from the "contrib" repository, which is open to community contributions.
Review the recipe at https://github.com/symfony/recipes-contrib/tree/main/shopware/app-bundle/1.0

Do you want to execute this recipe?
[y] Yes
[n] No
[a] Yes for all packages, only for the current installation session
[p] Yes permanently, never ask again for this project
(defaults to n): n
```


Modify the `SHOPWARE_APP_NAME` and `SHOPWARE_APP_SECRET` in the env to your app name`./.env` to ensure you can install the app in a store later.
Also, configure the `DATABASE_URL` to point to your database:

```sh
Expand Down Expand Up @@ -68,7 +87,7 @@ This will expose your Symfony server on a public URL, so the cloud store can com

The `manifest.xml` is the main interface definition between stores and your app server.
It contains all the required information about your app.
Let's start by filling in all the meta information:
Let's start by filling in all the meta-information:

```xml
// release/manifest.xml
Expand Down Expand Up @@ -113,7 +132,7 @@ The `<secret>` element is only present in development versions of the app. In pr

### Permissions

Permissions are needed as this app will need to read product descriptions and translate them:
The manifest needs permissions as this app will read product descriptions and translate them:

```xml
// release/manifest.xml
Expand Down Expand Up @@ -169,12 +188,11 @@ in its shops:
The timeout for the requests against the app server is 5 seconds.
:::

These four webhooks are provided by the App Bundle,
so the Bundle does for you the complete lifecycle and handling of Webhooks.
The App Bundle provides these four webhooks, so the Bundle does the complete lifecycle and handling of Webhooks for you.

## Handling shop events

To get started, let's write a simple Symfony event listener:
To get started, let's write a simple [Symfony event listener](https://symfony.com/doc/current/event_dispatcher.html#creating-an-event-listener):

```php
// src/EventListener/ProductWrittenWebhookListener.php
Expand Down Expand Up @@ -225,10 +243,10 @@ Now we can inspect the event payload:
### Fetching data from the shop

All `$entity.written` events contain a list of fields that a written event has changed.
The code above uses this information to determine if the description of a product was changed.
If the change did not affect the description, the listener early returns because there is nothing else to do for this event.
The code above uses this information to determine if someone changed the description of a product.
If the change does not affect the description, the listener early returns because there is nothing else to do with this event.

Now that it is certain that the description of the product was changed, we fetch the description through the API of the shop:
Now that it is certain that someone changed the description of the product, we fetch the description through the API of the shop:

```php
// src/EventListener/ProductWrittenWebhookListener.php
Expand Down Expand Up @@ -301,7 +319,7 @@ We will get to the generation of the hash later, but we need to check it first:

### Writing a translated description

Now that the app can be sure the description has not been translated before it can write the new description like so:
Now that the app can be sure, the description has not been translated before it can write the new description like so:

```php
// src/EventListener/ProductWrittenWebhookListener.php
Expand All @@ -326,11 +344,102 @@ Now that the app can be sure the description has not been translated before it c
}
```

Note that the hash of the original description gets saved as a value in the
custom fields of the product entity. This is possible without any further config since all custom fields are schema-less.
Note that the hash of the original description gets saved as a value in the custom fields of the product entity.
This is possible without any further config since all custom fields are schema-less.

The implementation of the `translate` method is disregarded in this example. You might perform an additional lookup through a translation API service to implement it.

## Complete Event Listener

```php
<?php declare(strict_types=1);

namespace App\EventListener;

use Shopware\App\SDK\HttpClient\ClientFactory;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Shopware\App\SDK\Context\Webhook\WebhookAction;
use Psr\Log\LoggerInterface;

#[AsEventListener(event: 'webhook.product.written')]
class ProductUpdatedListener
{
public function __construct(private readonly ClientFactory $clientFactory, private readonly LoggerInterface $logger)
{
}

public function __invoke(WebhookAction $action): void
{
$client = $this->clientFactory->createSimpleClient($action->shop);

$updatedFields = $action->payload[0]['updatedFields'];
$id = $action->payload[0]['primaryKey'];

if (!in_array('description', $updatedFields)) {
return;
}

$response = $client->post(
sprintf('%s/api/search/product', $action->shop->getShopUrl()),
[
'ids' => [$id],
'associations' => [
'translations' => [
'associations' => [
'language' => [
'associations' => [
'locale' => []
]
],
]
],
]
]
);
if (!$response->ok()) {
$this->logger->error('Could not fetch product', ['response' => $response->json()]);
return;
}

$product = $response->json()['data'][0];
$description = '';
$name = '';
foreach ($product['translations'] as $translation) {
if ($translation['language']['locale']['code'] === 'en-GB') {
$description = $translation['description'];
$name = $translation['name'];
}
}

$lastHash = $product['customFields']['translator-last-translation-hash'] ?? '';
if (md5($description) === $lastHash) {
return;
}

$response = $client->patch(sprintf('%s/api/product/%s', $action->shop->getShopUrl(), $id), [
'translations' => [
'en-GB' => [
'name' => $name,
'description' => 'Test English'
//'description' => $this->translate($description)
],
],
'customFields' => [
'translator-last-translation-hash' => md5($description)
]
]);

if (!$response->ok()) {
$this->logger->error('Could not update product', ['response' => $response->json()]);
}
}
}
```

## Connecting Doctrine to a Database

<<< @/docs/snippets/guide/app_database_setup.md

## Install the app

In this last step, we will install the app using the Shopware CLI tools.
Expand All @@ -344,7 +453,7 @@ shopware-cli project extension upload ProductTranslator/release --activate --inc
```

This command will create a zip file from the specified extension directory and upload it to your configured store.
The `--increase-version` parameter increases the version specified in the `manifest.xml` file. This flag is required so Shopware picks up changes made to the `manifest.xml` since the last installation.
The `--increase-version` parameter increases the version specified in the `manifest.xml` file. The app requires this flag so Shopware picks up changes made to the `manifest.xml` since the last installation.
Once successfully installed, you will see the app in the extension manager.
And when you save a product, the description will automatically update.

Expand Down
12 changes: 12 additions & 0 deletions snippets/guide/app_database_setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
The App Bundle ships with a basic Shop entity to store the shop information. You can extend this entity to store more information about your app if needed.

Symfony configures doctrine to use PostgreSQL by default. Change the `DATABASE_URL` environment variable in your `.env` file if you want to use MySQL.
You can also use SQLite by setting the `DATABASE_URL` to `sqlite:///%kernel.project_dir%/var/app.db` for development.

After choosing your database engine, you need to require two extra composer packages.

```shell
composer req symfony/maker-bundle migrations
```

And create your first migration using `bin/console make:migration` (which is using the `AbstractShop` Class) and apply it to your database with `bin/console doctrine:migrations:migrate`.
Loading