【Next.js×Laravel】StripeとPostgreSQLのデータを紐づけてTable一覧にする方法


# StripeとPostgreSQLに登録されているデータの紐づけ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1169664/bab2f1e4-fd0b-4c8e-b998-3a6d18b19e89.png)


Next.jsとLaravelとStripeを使って商品管理システムを作っている最中に、同じ商品をそれぞれ①Stripeと②PostgreSQLに登録しています。

しかも、①と②それぞれの商品IDが違うので、紐づけを行う必要があります。

このさき、商品の編集や削除といった機能を作るときにはStripe側の商品IDとPostgreSQL側の商品IDの対関係が必要だからです。

したがって、どのようにして異なる商品ID値を持った同じ商品をつもづけたのかを備忘録として残しておきます。

#### 【手順】
■Stripe側から商品を全件数分取得する
```frontend/app/products/getListProducts/page.tsx
    useEffect(() => {
        axios.get('http://localhost:8000/api/stripe/products')
            .then((response) => {
                setStripeProducts(response.data);
            });
    }, []);
```

■Laravel経由でPostgreSQLから商品を全件数分取得する
```frontend/app/products/getListProducts/page.tsx
    useEffect(() => {
        axios.get('http://localhost:8000/api/readAllProducts')
            .then((response) => {
                setProducts(response.data);
            });
    }, []);
```

■`map`関数と`find`関数を使ってStripe側とPostgreSQL側の①商品名(ProductName)と②商品説明(Product Description)が同じかを判定して新規で定義した配列に詰める

```frontend/app/products/getListProducts/page.tsx
    const mergeProductData = (stripeProducts: any[], products: any[]) => {
        return stripeProducts.map((stripeProduct) => {
            // PostgreSQLの商品からnameとdescriptionの両方で一致する商品を探す
            const matchedProduct = products.find(product =>
                product.name === stripeProduct.name &&
                product.description === stripeProduct.description
            );

            // `productId`をstripeProductに追加
            return {
                ...stripeProduct,
                productId: matchedProduct ? matchedProduct.productId : null, // 一致する場合は`productId`を追加
            };
        });
    }
```
上記の方法で、Stripe側とPostgreSQL側とのデータの紐づけができました。

```page.tsx
'use client'
import { useEffect, useState } from "react"
import axios from "axios"
import Header from "../productsComponents/header/page";
import { useRouter } from "next/navigation";

export default function AllListProducts() {
    const router = useRouter();
    const [stripeProducts, setStripeProducts] = useState<any[]>([]); // Stripeからの製品
    const [products, setProducts] = useState<any[]>([]); // PostgreSQLからの製品
    const [newProduct, setNewProduct] = useState({
        name: '',
        description: '',
        price: 0
    });

    // Stripeからの商品データを取得
    useEffect(() => {
        axios.get('http://localhost:8000/api/stripe/products')
            .then((response) => {
                setStripeProducts(response.data);
            });
    }, []);

    // PostgreSQLからの商品データを取得
    useEffect(() => {
        axios.get('http://localhost:8000/api/readAllProducts')
            .then((response) => {
                setProducts(response.data);
            });
    }, []);

    // 商品IDをStripe商品に追加する処理（name と description 両方を使って関連付け）
    const mergeProductData = (stripeProducts: any[], products: any[]) => {
        return stripeProducts.map((stripeProduct) => {
            // PostgreSQLの商品からnameとdescriptionの両方で一致する商品を探す
            const matchedProduct = products.find(product =>
                product.name === stripeProduct.name &&
                product.description === stripeProduct.description
            );

            // `productId`をstripeProductに追加
            return {
                ...stripeProduct,
                productId: matchedProduct ? matchedProduct.productId : null, // 一致する場合は`productId`を追加
            };
        });
    }

    // 両方のデータを結びつけて、productIdを追加した結果を取得
    const mergedProducts = mergeProductData(stripeProducts, products);

    // 商品編集ボタンのクリックハンドラー
    const clickProductEdit = () => {
        router.push(`/products/editProduct`);
    }

    // 商品削除ボタンのクリックハンドラー
    const clickProductDelete = () => {
        router.push('');
    }

    return (
        <div className="">
            <Header />
            <h1 className="font-bold">Product List</h1>
            <table className="w-full table-auto border border-gray-500 rounded-md">
                <thead className="bg bg-gray-100">
                    <tr>
                        <th className="p-4">ID</th>
                        <th className="p-4">Name</th>
                        <th className="p-4">Description</th>
                        <th className="p-4">
                            Price
                            <small>(USD)</small>
                        </th>
                        <th>Image</th>
                        <th>Edit</th>
                        <th>Delete</th>
                    </tr>
                </thead>
                <tbody>
                    {mergedProducts.length > 0 ? (
                        mergedProducts.map((product) => (
                            <tr key={product.id}>
                                <td className="p-4">{product.id}</td>
                                <td className="p-4">{product.name}</td>
                                <td className="p-4">{product.description}</td>
                                <td className="p-4">{product.price}</td>
                                <td className="p-4">
                                    {product.images ? (
                                        <img src={product.images[0]} style={{ width: 100 }} />
                                    ) : null}
                                </td>
                                <td className="p-4">
                                    <button
                                        type="button"
                                        className="bg bg-blue-500 text-white rounded-md px-4 py-4"
                                        onClick={clickProductEdit}
                                    >
                                        Edit
                                    </button>
                                </td>
                                <td className="p-4">
                                    <button
                                        type="button"
                                        className="bg bg-rose-500 text-white rounded-md px-4 py-4"
                                        onClick={clickProductDelete}
                                    >
                                        Delete
                                    </button>
                                </td>
                            </tr>
                        ))
                    ) : (
                        <tr>
                            <td colSpan={7} className="text-center p-4">
                                <p>Products are not register</p>
                            </td>
                        </tr>
                    )}
                </tbody>
            </table>
        </div>
    )
}
```