PHPackages                             rizal/management-login - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. rizal/management-login

ActiveProject[Framework](/categories/framework)

rizal/management-login
======================

Management Login MVC

v1.0.0(3y ago)37PHPPHP &gt;=8

Since Feb 4Pushed 3y ago1 watchersCompare

[ Source](https://github.com/RizalFIrdaus/PHP-Login-Management)[ Packagist](https://packagist.org/packages/rizal/management-login)[ RSS](/packages/rizal-management-login/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (1)Versions (2)Used By (0)

Login Management (Architecture MVC - Repository Pattern - Facades Pattern - Singleton Pattern)
==============================================================================================

[](#login-management-architecture-mvc---repository-pattern---facades-pattern---singleton-pattern)

> About The results of learning the basics of PHP programming language, object-oriented programming (OOP), unit testing, web PHP, Composer, MVC, logging, and software development architecture techniques have been documented in this case study for learning purposes only

How to install the project
==========================

[](#how-to-install-the-project)

Create project via composer

```
composer create-project rizal/management-login
```

If your not have composer, u can create project via github clone

```
git clone https://github.com/RizalFIrdaus/PHP-Login-Management.git
```

If your not have git on your local computer, you can download manually in github (Download ZIP)

Setup
=====

[](#setup)

Dependency Composer
-------------------

[](#dependency-composer)

After installing the project, you may have this project without vendor dependency, insert dependency like down below

```
composer install
composer dump-autoload
```

Dump-autoload is optional if your changes any autoload in composer.json

Database
--------

[](#database)

This project is required mysql databases, to config database environment, you can open env folder then open **database.php**To configure the database in the *getDataConfig* function, if it is only used for production, the database used is *prod*. However, if you want to perform testing implementation, the configuration must also include the *test* database

```
"database" => [
            "prod" => [
                "url" => "mysql:host=localhost:3306;dbname=php_login_management",
                "username" => "root",
                "password" => ""
            ],
            "test" => [
                "url" => "mysql:host=localhost:3306;dbname=php_login_management_test",
                "username" => "root",
                "password" => ""
            ]
        ]
```

### Query Create Database &amp; Tables Prod&amp;Test

[](#query-create-database--tables-prodtest)

After configuration, you will need to create the database and tables that are appropriate for the scope of this project. Here is a query for executing SQL database for testing and production

```
CREATE DATABASE php_login_management;
CREATE DATABASE php_login_management_test;

CREATE TABLE users(
    id VARCHAR(255) PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL
)
CREATE TABLE sessions(
    id VARCHAR(255) PRIMARY KEY,
    user_id VARCHAR(255) NOT NULL,
)
ALTER TABLE sessions
ADD CONSTRAINT fk_session_user
FOREIGN KEY (user_id)
REFERENCES users(id);
```

Running Project
===============

[](#running-project)

To run this project :

```
php -S localhost:8080 -t public
```

The Project can run with port 8080 if your set port like step before

Roadmap
=======

[](#roadmap)

- [PHP-Login-Management](#login-management-architecture-mvc---repository-pattern)
    - [Structure Database](#Structure-Database)
    - [Architecture Software](#architecture-software)
    - [Database GetConnnection](#database-getconection)
        - [Environment Database](#env)
        - [Method GetConnection](#getconnection-function)
        - [Testing Connection](#testing-connection)
    - [Templating View](#database-getconection)
        - [Testing View](#testing-view)
    - [Repository](#repository)
        - [Domain](#domain)
        - [User Repository](#user-repository)
        - [Testing User Repository](#testing-user-repository)
    - [Service](#service)
        - [Registration Service](#registration-service)
        - [Testing User Service](#testing-user-service)
    - [User Controller](#user-controller)
        - [User Controller Method](#user-controller)
        - [Testing User Controller](#testing-user-controller)

Structure Database
------------------

[](#structure-database)

We have 2 databases : (1) php\_login\_management and (2) php\_login\_management\_test

Database (1) is original based for raw data, while database (2) is replica from database (1) that contains dirty raw data for used testing

```
CREATE DATABASE php_login_management;
CREATE DATABASE php_login_management_test;

CREATE TABLE users(
    id VARCHAR(255) PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL
)
CREATE TABLE sessions(
    id VARCHAR(255) PRIMARY KEY,
    user_id VARCHAR(255) NOT NULL,
)
ALTER TABLE session
ADD CONSTRAINT fk_session_user
FOREIGN KEY (user_id)
REFERENCES users(id);
```

Architecture Software
---------------------

[](#architecture-software)

MVC - Repository Pattern

Database GetConection
---------------------

[](#database-getconection)

The creation of getConnection uses a singleton architecture, which means creating an object once and using it multiple times. The getConnection function will accept a "prod" or "test" parameter. The default is set to "test"

### Env

[](#env)

```
function getDataConfig(): array
{
    return [
        "database" => [
            "prod" => [
                "url" => "mysql:host=localhost:3306;dbname=php_login_management",
                "username" => "root",
                "password" => ""
            ],
            "test" => [
                "url" => "mysql:host=localhost:3306;dbname=php_login_management_test",
                "username" => "root",
                "password" => ""
            ]
        ]
    ];
}
```

### getConnection Function

[](#getconnection-function)

```
public static function getConnection(string $env = "test"): \PDO
    {
        // Singleton architecture (User single to many action)
        // if pdo null then create new db if not .. just return back pdo had been created
        if (self::$pdo == null) {
            // create new database
            require_once __DIR__ . "/../../env/database.php";
            $config = getDataConfig();
            self::$pdo = new PDO(
                $config["database"][$env]["url"],
                $config["database"][$env]["username"],
                $cofing["database"][$env]["password"]
            );
        }
        return self::$pdo;
    }
```

### Testing Connection

[](#testing-connection)

During testing, two data connection tests will be performed to ensure that the data is not null and to verify that the implemented singleton architecture is functioning properly.

```
    public function testConnection()
    {
        $connection = Database::getConnection();
        self::assertNotNull($connection);
    }

    public function testSingletonDatabase()
    {
        $con1 = Database::getConnection();
        $con2 = Database::getConnection();
        self::assertSame($con1, $con2);
    }
```

The Result

```
    PHPUnit 9.5.8 by Sebastian Bergmann and contributors.
..                                                                  2 / 2 (100%)
Time: 00:00.054, Memory: 4.00 MB
OK (2 tests, 2 assertions)
```

View Templating
---------------

[](#view-templating)

Templating is used to separate the header and footer for clean code in the visual aspect. By separating the header, footer, and body, we only need to import the header and footer while the body is always changing

```
    public static function render(string $view, $model)
    {
        require __DIR__ . "/../View/Layouts/header.php";
        require __DIR__ . '/../View/' . $view . '.php';
        require __DIR__ . "/../View/Layouts/footer.php";
    }
```

### Testing View

[](#testing-view)

This testing will check the expected regex displayed on the website page.

```
    public function testRender()
    {
        View::render("Home/index", [
            "title" => "Login"
        ]);
        self::expectOutputRegex("[Login]");
        self::expectOutputRegex("[Register]");
    }
```

The Result

```
PHPUnit 9.5.8 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 00:00.067, Memory: 4.00 MB

OK (1 test, 1 assertion)
```

Repository
----------

[](#repository)

This architecture uses the MVC architecture and is combined with the Repository Pattern to avoid overwhelming logic in the MVC Controller. The Controller will call the Service, the Service will call the Repository, and the Repository will retrieve data from the Domain and directly access the database

### Domain

[](#domain)

The Domain represents the table data in the database.

```
class User
{
    private string $id;
    private string $name;
    private string $password;

    // SETTER AND GETTER
    public function getId(): string
    {
        return $this->id;
    }
    public function getName(): string
    {
        return $this->name;
    }
    public function getPassword(): string
    {
        return $this->password;
    }
    public function setId(string $id): void
    {
        $this->id = $id;
    }
    public function setName(string $name): void
    {
        $this->name = $name;
    }
    public function setPassword(string $password): void
    {
        $this->password = $password;
    }
}
```

### User Repository

[](#user-repository)

The User Repository has several methods such as save for saving data into the database, getById for retrieving data based on Id, and deleteAll for testing purposes

```
class UserRepository
{
    private \PDO $connection;

    public function __construct(\PDO $connection)
    {
        $this->connection = $connection;
    }

    // Save user into database
    public function save(User $user): User
    {
        $statement = $this->connection->prepare("INSERT INTO users(id,name,password) VALUES (?,?,?)");
        $statement->execute([
            $user->getId(),
            $user->getName(),
            $user->getPassword()
        ]);
        return $user;
    }

    public function getById(string $id): ?User
    {
        $statement = $this->connection->prepare("SELECT id,name,password FROM users WHERE id=?");
        $statement->execute([$id]);

        // Trying to fetch data from id
        try {
            if ($row = $statement->fetch()) {
                $user = new User();
                $user->setId($row["id"]);
                $user->setName($row["name"]);
                $user->setPassword($row["password"]);
                return $user;
            } else {
                return null;
            }
        } finally {
            // Close Query
            $statement->closeCursor();
        }
    }

    public function deleteAll(): void
    {
        $this->connection->exec("DELETE FROM users");
    }
}
```

### Testing User Repository

[](#testing-user-repository)

Testing the User Repository will check if method user has saved data into the database and the second one will check if method getById is null

```
    private UserRepository $userRepository;
    public function setUp(): void
    {
        $this->userRepository = new UserRepository(Database::getConnection());
        $this->userRepository->deleteAll();
    }

    public function testSaveSuccess()
    {
        $user = new User();
        $user->setId("rizal");
        $user->setName("rizal");
        $user->setPassword("rahasia");

        $this->userRepository->save($user);
        $result = $this->userRepository->getById($user->getId());
        self::assertNotNull($result);
        self::assertEquals($user->getId(), $result->getId());
    }

    public function testIdNotFound()
    {
        $user = $this->userRepository->getById("awdawd");
        self::assertNull($user);
    }
```

The Result

```
PHPUnit 9.5.8 by Sebastian Bergmann and contributors.

..                                                                  2 / 2 (100%)

Time: 00:00.033, Memory: 4.00 MB

OK (2 tests, 3 assertions)
```

Service
-------

[](#service)

The Service is the centralized business logic. The Service will call the Repository to use available methods. The Repository calls the Domain and is directly connected to the database.

### Registration Service

[](#registration-service)

The registration method will validate incoming requests, which will be handled by the validationUserRegistrationRequest. If it passes the registration stage, it will then check if the request ID has already been created. If it has, it will give an exception, otherwise, a new user will be created. This method will provide a response in the form of user data by creating a UserRegistrationResponse model, while the UserRegistrationRequest will handle the incoming request in the user registration request

```
 public function register(UserRegistrationRequest $request): UserRegistrationResponse
    {
        // Validation
        $this->validationUserRegistrationRequest($request);

        try {
            Database::beginTransaction();
            $user = $this->userRepository->getById($request->id);
            // Checking if user already exist
            if ($user != null) {
                throw new ValidationException("Id $request->id already exist !");
            }
            // Create new user if id ready to save
            $user = new User();
            $user->setId($request->id);
            $user->setName($request->name);
            $user->setPassword(password_hash($request->password, PASSWORD_BCRYPT));
            $this->userRepository->save($user);

            // Return response
            $response = new UserRegistrationResponse();
            $response->user = $user;
            return $response;

            Database::commitTransaction();
        } catch (\Exception $exception) {
            Database::rollbackTransaction();
            throw $exception;
        }
    }
```

validationUserRegistrationRequest

```
public function validationUserRegistrationRequest(UserRegistrationRequest $request)
    {
        if (
            $request->id == null || $request->name == null || $request->password == null ||
            trim($request->id) == "" || trim($request->name) == "" || trim($request->password) == ""
        ) {
            throw new ValidationException("Id,name or password can't blank", 403);
        } else if (strlen($request->id) password) id = "rizal300500";
        $request->name = "Rizal";
        $request->password = "rahasia123";
        $response = $this->userService->register($request);
        // Checking response not null
        self::assertNotNull($response);
        // Checking request id equals response id
        self::assertEquals($request->id, $response->user->getId());
        // Verify hashing password
        self::assertTrue(password_verify($request->password, $response->user->getPassword()));
    }
    public function testServiceFailed()
    {
        $this->expectException(ValidationException::class);
        $request = new UserRegistrationRequest();
        $request->id = "";
        $request->name = "";
        $request->password = "";
        $this->userService->register($request);
    }

    public function testServiceDuplicate()
    {
        $user = new User();
        $user->setId("esan300500");
        $user->setName("Rizal");
        $user->setPassword("rahasia123");
        $this->repository->save($user);

        $this->expectException(ValidationException::class);

        $request = new UserRegistrationRequest();
        $request->id = "esan300500";
        $request->name = "Rizal";
        $request->password = "rahasia123";
        $this->userService->register($request);
    }
```

The Result

```
PHPUnit 9.5.8 by Sebastian Bergmann and contributors.

...                                                                 3 / 3 (100%)

Time: 00:00.191, Memory: 4.00 MB

OK (3 tests, 5 assertions)
```

User Controller
---------------

[](#user-controller)

The Controller is handled by the UserController that implements some methods such as the index method, which centralizes the index route and passes it the get method, and the store method, which centralizes the store route and passes it the post method to store data from the client.

```
 public function store()
    {
        // Request get from method post
        $request = new UserRegistrationRequest();
        $request->id = $_POST["id"];
        $request->name = $_POST["name"];
        $request->password = $_POST["password"];

        // trying to call register service if it success then redirecting to user/login
        // else render view register with error message from exception get message
        try {
            $this->userService->register($request);
            // Redirect::to("/users/login");
        } catch (ValidationException $exception) {
            View::render("User/register", [
                "title" => "Register User",
                "error" => $exception->getMessage()
            ]);
        }
    }
```

### Testing User Controller

[](#testing-user-controller)

This testing runs five scenario: view index ,store success,store null, store failed, and store duplicate ID

```
 public function testRegister()
    {
        $this->userController->index();
        $this->expectOutputRegex("[Register]");
        $this->expectOutputRegex("[Muhammad Rizal Firdaus]");
        $this->expectOutputRegex("[Name]");
        $this->expectOutputRegex("[Id]");
        $this->expectOutputRegex("[Password]");
    }

    public function testStoreRegisterSuccess()
    {
        $_POST["id"] = "rizal300500";
        $_POST["name"] = "rizal";
        $_POST["password"] = "rahasia12345";
        $this->userController->store();
        $this->expectOutputRegex("[]");
    }

    public function testStoreRegisterFailedBlank()
    {
        $_POST["id"] = "";
        $_POST["name"] = "";
        $_POST["password"] = "";
        $this->userController->store();
        $this->expectOutputRegex("[Id,name or password can't blank]");
    }

    public function testStoreRegisterFailedNull()
    {
        $this->userController->store();
        $this->expectOutputRegex("[Id,name or password can't blank]");
    }

    public function testStoreRegisterDuplicate()
    {
        $user = new User();
        $user->setId("rizal300500");
        $user->setName("Rizal");
        $user->setPassword("rahasia12345");
        $this->repository->save($user);

        $_POST["id"] = "rizal300500";
        $_POST["name"] = "Rizal";
        $_POST["password"] = "rahasia12345";
        $this->userController->store();
        $this->expectOutputRegex("[Id rizal300500 already exist !]");
    }
```

The Result

```
PHPUnit 9.5.8 by Sebastian Bergmann and contributors.

.....                                                               5 / 5 (100%)

Time: 00:00.116, Memory: 4.00 MB

OK (5 tests, 5 assertions)
```

### Built By

[](#built-by)

Muhammad Rizal Firdaus

###  Health Score

22

—

LowBetter than 22% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity8

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 98.4% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

1197d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/e025aa0548d7b58a499074da21140960200f7930fdc2c8245024766a9034e55a?d=identicon)[RizalFIrdaus](/maintainers/RizalFIrdaus)

---

Top Contributors

[![RizalFIrdaus](https://avatars.githubusercontent.com/u/51688102?v=4)](https://github.com/RizalFIrdaus "RizalFIrdaus (60 commits)")[![khannedy](https://avatars.githubusercontent.com/u/70091?v=4)](https://github.com/khannedy "khannedy (1 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/rizal-management-login/health.svg)

```
[![Health](https://phpackages.com/badges/rizal-management-login/health.svg)](https://phpackages.com/packages/rizal-management-login)
```

###  Alternatives

[laravel/telescope

An elegant debug assistant for the Laravel framework.

5.2k67.8M192](/packages/laravel-telescope)[spiral/roadrunner

RoadRunner: High-performance PHP application server and process manager written in Go and powered with plugins

8.4k12.2M84](/packages/spiral-roadrunner)[nolimits4web/swiper

Most modern mobile touch slider and framework with hardware accelerated transitions

41.8k177.2k1](/packages/nolimits4web-swiper)[laravel/dusk

Laravel Dusk provides simple end-to-end testing and browser automation.

1.9k36.7M259](/packages/laravel-dusk)[laravel/prompts

Add beautiful and user-friendly forms to your command-line applications.

708181.8M596](/packages/laravel-prompts)[cakephp/chronos

A simple API extension for DateTime.

1.4k47.7M121](/packages/cakephp-chronos)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
