简体   繁体   English

如何增加/减少购物车中产品的数量?

[英]How to increase / decrease the quantity of a product in a shopping cart?

I have some difficulty implementing the logic to add or remove a product from my cart.我在实现从购物车中添加或删除产品的逻辑时遇到了一些困难。

First of all at the level of my database:首先在我的数据库级别:

  • Each basket consists of: a basket ID, a product ID, a customer ID and a quantity.每个购物篮包括:购物篮 ID、产品 ID、客户 ID 和数量。

This means that the user has in fact "several baskets" (One product = One basket)这意味着用户实际上有“几个篮子”(一个产品=一个篮子)

BACK-END后端

Here is how I created my models / relation with sequelize:以下是我如何使用 sequelize 创建模型/关系:

 // Inside db.config.js db.paniers = sequelize.define('panier', { id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true, field: 'PAN_ID' }, userId: { type: Sequelize.INTEGER, references: { model: db.users, key: 'USE_ID' }, primaryKey: true, allowNull: false, field: 'USE_ID' }, produitId: { type: Sequelize.INTEGER, references: { model: db.produits, key: 'PRO_ID' }, primaryKey: true, allowNull: false, field: 'PRO_ID' }, quantite: { type: Sequelize.INTEGER, allowNull: false, field: 'PAN_QUANTITE' } }, { tableName: 'PANIER' });

Then for queries, two queries are made:然后对于查询,进行两个查询:

  • One to display information about the cart (cart ID, product ID, product name, product price, product image, cart quantity)一个显示关于购物车的信息(购物车 ID、产品 ID、产品名称、产品价格、产品图片、购物车数量)

  • Another one to update the quantity.另一个更新数量。

Here is how I made my axios queries and the result under POSTMAN这是我如何进行 axios 查询和 POSTMAN 下的结果

 const APIURL = 'http://localhost:8090/api'; // Get the details of the cart export const getDetails = (userId) => axios.get(`${APIURL}/panier/details/${userId}`, { userId: userId, }); // Update the quantity of the cart export const updateQuantite = (produitId) => axios.put(`${APIURL}/panier/${produitId}`, { produitId: produitId, });

 // Result for the userId 1 (getDetails) { "PRO_ID": 1, "PRO_NOM": "Un immeuble", "PRO_PRIX": "1515", "PRO_URL": "58afa4f2-41b1-42f7-a371-6d267784c44e.jpg", "PAN_QUANTITE": 1, "PAN_ID": 1 }, { "PRO_ID": 2, "PRO_NOM": "Model", "PRO_PRIX": "102", "PRO_URL": "a76fbe76-a183-49fa-84ee-40d5da08b91f.png", "PAN_QUANTITE": 1, "PAN_ID": 2 }

And here are my two controllers managing his routes:这是我的两个控制器管理他的路线:

 // Display the informations of the basket exports.getDetails = (req, res) => { const queryResult = db.sequelize.query( 'SELECT P.PRO_ID, PRO_NOM, PRO_PRIX, PRO_URL, PA.PAN_QUANTITE, PA.PAN_ID\n' + 'FROM panier AS PA INNER JOIN produit AS P ON PA.PRO_ID = P.PRO_ID\n' + 'WHERE USE_ID =:id', { replacements: { id: req.params.userId }, type: QueryTypes.SELECT } ).then(panier => { res.json(panier); }).catch(err => res.status(400).send(err)); } // Modify the quantity of a basket exports.update = (req, res) => { Paniers.update({ quantite: req.body.quantite }, { where: { produitId: req.params.produitId } }).then(panier => { res.json(panier); }).catch(err => res.status(400).send(err)); }

FRONT-END前端

This is how my information is displayed (Still under development, that's why it really doesn't look like anything ^^' )这就是我的信息的显示方式(仍在开发中,这就是为什么它看起来不像任何东西^^')

为用户显示 2 个篮子,带有 2 个按钮:加号和减号

This is where I get lost... Here is my shopping cart pagePage.js:这是我迷路的地方......这是我的购物车pagePage.js:

 import React, { useState, useEffect } from 'react'; import { Card, CardHeader, CardMedia, Grid, ButtonGroup, Button} from '@material-ui/core'; import PayPal from '../services/PayPal/paypal' import {getDetails, updateQuantite, getAllPanier, get} from '../services/API/panier' export default function PanierPage() { // Récupération des détails des paniers const [paniers, setPaniers] = useState([]) const getPaniersDetails = () => [ getDetails(JSON.parse(localStorage.getItem('User')).id).then(response => { setPaniers(response.data) console.log(response) }).catch(err => console.log(err)) ] const handleIncrement = (id) => { updateQuantite(id).then(response => { //??? }).catch(err => console.log(err)) } const handleDecrement = () => { } // Affichage des détails des paniers const paniersAffichage = paniers.map((panier) => ( <Grid container> <Card key={panier.PAN_ID}> <CardHeader title={panier.PRO_NOM}/> <CardMedia image={`http://localhost:8090/${panier.PRO_URL}`}/> <Button onClick={() => handleIncrement(panier.PRO_ID)}> + </Button> {panier.PAN_QUANTITE} <Button onClick={handleDecrement}> - </Button> </Card> </Grid> )); // Chargement des produits useEffect(() => { getPaniersDetails(); }, []) return ( <> <Grid> {paniersAffichage} </Grid> <PayPal/> </> ); }

For explanations:解释:

  • I get my basket information in 'getPaniersDetails' where I indicate the user id then I load it in my useEffect.我在“getPaniersDetails”中获取我的购物篮信息,我在其中指出用户 ID,然后将其加载到我的 useEffect 中。

  • basketsDisplay allows me to display the baskets of the user concerned. basketsDisplay 允许我显示相关用户的购物篮。

  • I give in each card the ID of the cart for the mapping, then I display the information... When clicking on "+" I want to increase my quantity, so I give it the product ID.我在每张卡片中为映射提供购物车的 ID,然后显示信息……点击“+”时,我想增加数量,所以我给它提供了产品 ID。

  • handleIncrement will therefore handle this action, using 'updateQuantite'.因此,handleIncrement 将使用“updateQuantite”处理此操作。

  • This is where I block, I have the impression of mixing myself between my different IDs.这就是我阻止的地方,我觉得自己在不同的 ID 之间混在一起。 Particularly between the cart ID of the table and the cart ID of my query (SELECT)特别是在表的购物车 ID 和我的查询(SELECT)的购物车 ID 之间

I'm sure it's something very simple to set up but in my head it seems complicated to me...我敢肯定,设置起来非常简单,但在我看来,这对我来说似乎很复杂......

If I missed any important points tell me, I will do my best to change my post如果我错过了任何重要的点告诉我,我会尽力改变我的帖子

Your object model doesn't make much sense.您的 object model 没有多大意义。

I would change it to make it more dynamic.我会改变它以使其更具活力。

If you instead have users, and they have a basket property, this approach makes more sense.如果您有用户,并且他们有一个购物篮属性,那么这种方法更有意义。

NOTE: The code below is a rough outline, but should give you an idea of how to do it.注意:下面的代码是一个粗略的大纲,但应该让您知道如何去做。

interface Database {
    users: User[]; // Array of users.
}

interface User {
    id: number;
    username: string;
    passwordHash: string;
    baskets: Basket[];
}

interface Basket {
    id: number;
    items: Item[]; // array of items;
    date: string;
}

interface Item {
    id: number; // ID of the item.
    name: string;
    imgURL: string;
    description: string[];
    quantity: number;
}

Now if we want to receive and send data to the database, we would do so like this.现在,如果我们想要接收数据并将数据发送到数据库,我们会这样做。

interface Basket {
    items: string[]; // list of item id's.
}

// You want to use a session token instead of the user id so noone but the user can access their basket. 
// Normaly baskets will be local to the browser or app and not sored on a servers database. 
// Only past orders should be stored. But in this example, we are storing the current basket too.
async function getBasket(sessionToken: string){
    return await axios.get(`${api.host}/basket`, { 
        headers: {
            Session-Token: sessionToken, // used to identify the user
            Content-Type: "application/json",
        }
    }).data;
}

// we send the basket object, which is just a list of IDs, and the session token.
async function setBasket(basket: Basket, sessionToken: string){
    return await axios.put(`${api.host}/basket`, { 
        headers: {
            Session-Token: sessionToken, // used to identify the user
            Content-Type: "application/json",
        }
    }).data;
}

Now on the server side, with express we can handle the requests.现在在服务器端,我们可以使用 express 处理请求。

To implement sessions with express, there is the npm module express-session which is a middleware for express.要使用 express 实现会话,有 npm 模块express-session ,它是 express 的中间件。 When the user logs in, they will be given a header that they will save as a cookie to use on their future requests.当用户登录时,他们将获得一个 header,他们将把它保存为 cookie 以用于他们未来的请求。 When they logout, the session is removed from your server and the cookie is deleted on the client.当他们注销时,session 将从您的服务器中删除,并且 cookie 在客户端上被删除。

For added security, you can set an expiration on the session.为了增加安全性,您可以在 session 上设置过期时间。 The user will have to re-login and get a new session.用户必须重新登录并获得新的 session。


// check the documentation to tune it to what you need.
app.use(session({
  secret: 'mySuperSecret',
  resave: false,
  saveUninitialized: true,
  cookie: { secure: true }
}))

app.get("/basket", async(req, res) => {
    // here we first check if the session exists.
    if(req.session.views){
        // if it does, then we return the list of baskets.
        const { baskets } = await getUser(req.session.userID);
        res.sendStatus(200).send([success: true, data: baskets]);
    }
    // if not, then we will return a 403 error.
    // we also send a response that matches the layout of the normal response.
    res.sendStatus(403).send([success: false, data: []]);
    
})

app.put("/basket", (req, res) => {
    // here we first check if the session exists.
    if(req.session.views){
        // if it does, then we add the basket to the user.
        addBasket(req.session.userID, basket)
            .then(res.sendStatus(204).send([success: true, data: []]))
    }
    // if not, then we will return a 403 error.
    // we also send a response that matches the layout of the normal response.
    res.sendStatus(403).send([success: false, data: []]);
})

If you have any question, just ask in the comment section.如果您有任何问题,请在评论部分提出。 I will respond when I am free.我有空时会回复。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM