Bonus: revise layer programming

Lab 5
Author

Ludovic Deneuville

Objectives

For the moment, this application simply contains a list of players.

The aim will be to create a feature that will allow players to register for tournaments.

We will proceed step by step.

Before you start

You will work on a fork or a copy of the repo ENSAI-2A-projet-info-template.

1 Initialisation of views

If this item is selected, you must now access the list of tournaments in a new view.

It will display the list of tournaments and allow the user to choose which one they want to register for.

NoteHow to obtain the list of tournaments?
  • You will need a service method that returns the list of all tournaments
    • TournoiService.lister_tous() -> list[Tournoi]
  • The list of tournaments will be available in the database
    • a service method should not communicate directly with a database; that is the role of a DAO
    • The dao method TournoiDao.lister_tous() -> list[Tournoi] will communicate with the database to transfer the list of tournaments to the service method
  • To transfer tournament lists, you must also create Tournoi business object

Now we are beginning to get an idea of the methods and objects to create.

2 Create the business object

Let us begin by creating the Tournoi business object.

Business objects mainly contain attributes and few methods. They are primarily used to transfer information between layers.

    • attributes: id_tournoi, nom, ville, capacite_max
    • add a constructor with all attributes

3 Map it with a database table

Usually, the business object class and its attributes correspond to the table and columns in the database.

Assuming that a player can participate in several tournaments and that a tournament can have several players, it is therefore necessary to create a table to keep track of which player is participating in which tournament.

CREATE TABLE tournoi (
    id_tournoi     SERIAL PRIMARY KEY,
    nom            VARCHAR(100) NOT NULL,
    ville          VARCHAR(100) NOT NULL,
    capacite_max   INT CHECK (capacite_max > 0)
);

CREATE TABLE inscription (
    id_inscription SERIAL PRIMARY KEY,
    id_tournoi     INT NOT NULL REFERENCES tournoi(id_tournoi),
    id_joueur      INT NOT NULL REFERENCES joueur(id_joueur),
    UNIQUE(id_tournoi, id_joueur) -- un joueur ne peut pas s inscrire deux fois au meme tournoi
);

4 DAO

We now need the DAO method, which allows us to retrieve the list of tournaments from the database.

5 Service

The view does not communicate directly with the DAO. It must request the service class.

Even though in this case, the service method simply flat passes: it receives a tournament list and returns that same list.

6 Back to the view

Great, we now have a service method that allows us to obtain a list of all tournaments. We will display it so that players can choose.

In view InscriptionTournoiVue:

If they change their mind, players can also return to the previous menu without registering for any tournament.

NoteIf a tournament has been selected

You will need to manage the registration:

  • JoueurService.s_inscrire_tournoi(tournoi) -> bool
  • This service must verify that the registration is valid
  • If so, a row will be created in the inscription table
    • Therefore, the service will need to use the dao method InscriptionDao.creer(joueur, tournoi) -> bool

7 Methods needed

In this method we will call InscriptionDao.creer(joueur, tournoi) -> bool.

This method requires two parameters:

  • tournoi: easy to get it, it is a parameter of calling method s_inscrire_tournoi(tournoi)
  • joueur: how to get it?
joueur_service.py
class JoueurService:
    ...

    def s_inscrire_tournoi(self, tournoi) -> bool:

        ...

        succes = InscriptionDao.creer(joueur, tournoi)

This is where the Session class comes into play.

7.1 Session

When a player is logged in, we must have access to their information at all times.

This is the role of the Session class.

So we can get the player using Session().joueur

7.2 Back to service method s_inscrire_tournoi()

joueur_service.py
from view.session import Session

class JoueurService:
    ...

    def s_inscrire_tournoi(self, tournoi) -> bool:

        ...

        joueur = Session().joueur
        succes = InscriptionDao.creer(joueur, tournoi)

Great, we have everything we need to call the dao method to create an Inscription, but aren’t there a few things we should check first?

What if the player is already registered?

Now let’s check that the tournament is not already full:

8 Let’s finish the view

To summarise the flows

TipIf you have to create a webservice

The principle would be broadly the same. You will need the same services, but instead of going through views:

  • create a endpoint GET /tournament to display the list
  • create a endpoint POST /registration body: {player_id: ?, tournament_id: ?}

sequenceDiagram
    participant Vue
    participant Service
    participant Dao
    participant BDD

    Note over Vue: MenuJoueurVue
    Vue->>Vue: Choice "S'inscrire à un tournoi"
    Note over Vue: InscriptionTournoiVue
    Vue->>Service: TournoiService.lister_tous()
    Note over Service: TournoiService
    Service->>Dao: TournoiDao.lister_tous()
    Dao->>BDD: SELECT * FROM tournoi
    BDD-->>Dao:  tournois JSON
    Dao-->>Service: list[Tournoi]
    Service-->>Vue: list[Tournoi]
    Vue->>Vue: Affiche la liste, option Retour<br> Attend une sélection
    Vue->>Service: JoueurService.s_inscrire_tournoi(tournoi)
    Note over Service: JoueurService
    Service->>Dao: InscriptionDao.est_inscrit(joueur,tournoi)
    Dao->>BDD: SELECT COUNT(*)<br> FROM inscription<br> WHERE id_joueur=? <br>AND id_tournoi=?
    BDD-->>Dao: true/false
    Dao-->>Service: true/false
    Service-->>Vue: if true, back to MenuJoueurVue
    Service->>Dao: TournoiDao.est_complet(tournoi)
    Dao->>BDD: SELECT FROM tournoi, inscription
    BDD-->>Dao: true/false
    Dao-->>Service: true/false
    Service-->>Vue: if false, back to MenuJoueurVue
    Service-->>Dao: InscriptionDao.creer(tournoi, joueur)
    Dao->>BDD: INSERT INTO inscription
    BDD-->>Dao: true/false
    Dao-->>Service: true/false
    Service-->>Vue: back to MenuJoueurVue