diff --git a/README.md b/README.md index 2cd2d9e611f373d4603e0f8feca694bc37259dff..c1a99125cafe24ad631d036585fac13a96011195 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,112 @@ # Simple E-commerce System -## Project Structure -You can see it on the structure file. - ## Overview -This is a simple project designed to develop diverse competencies such as routing, MVC (Model-View-Controller) architecture and database connection. -It was mainly developed with PHP, Html, mysql and TablePlus (GUI) for the database. -There is also a small usage of javascript function for form validation. - -## Entry Point and Navigation -The index.php file is the entry point of the application. -The project is mainly composed of 4 pages or views: Homepage, Products page, Add Product and Edit Product. -It includes a simple router to manage the navigation between the different pages, which makes use of two different controllers the HomeController and the ProductController. - -## Controllers -The system has two Controllers: the HomeController and the ProductController. The HomeController is simply used to show the HomePage. -While the ProductController manages every operation that involves products such as showing the product pages or even call the methods to update the database. -Notice that Operations that interact with the database themselves are inside the Products class. +This project is a simple e-commerce system built with PHP. It includes functionalities for managing products, users, addresses, and profiles. The system is structured to separate concerns into different directories for better organization and maintainability. +It was built under a training premise for Sage, a software company. +It's main goal was to develop PHP skills and other adjacent ones such as git and mysql. -## Database -We have a Database class that simply establishes the parameters to connect to the database and also has a query method that it's called when it's necessary to update the database in some sense. - -### Method: `query($query, $params = [])` - -The `query` method prepares and executes an SQL query with optional parameters. +## Project Structure +The project is structured as follows: -#### Parameters -- **`$query`**: The SQL query to be executed. -- **`$params`**: An associative array of parameters to bind to the query. The keys should match the named placeholders in the SQL query. +/simple-ecommerce-system + /core + App.php + Container.php + functions.php + Router.php + Flash.php + Mailer.php + /models + Product.php + User.php + Address.php + Country.php + /db + /migrations + /seeds + Database.php + /views + /partials + header.html + nav.html + footer.html + /products + create.view.php + index.view.php + edit.view.php + show.view.php + /users + login.php + register.php + edit.user.php + reset.password.php + show.all.php + /profile + edit.profile.php + profile.php + /controllers + HomeController.php + ProductsController.php + UserController.php + ProfileController.php + LoginController.php + AddressController.php + /web + routes.php + /public + index.php + /css + styles.css + /images + logo.png + /js + confirmDelete.js + validateForm.js + bootstrap.php -#### Functionality -1. **Prepare the SQL Statement**: The method uses the `prepare` function of the PDO connection to create a prepared statement. -2. **Bind Parameters**: It iterates over the `$params` array and binds each parameter to the prepared statement using `bindValue`. This ensures that the values are safely inserted into the query, preventing SQL injection. -3. **Execute the Statement**: The prepared statement is executed with the bound parameters. -4. **Return the Statement**: The executed statement is returned, allowing further operations such as fetching results. +## Installation +1. Clone the repository: `git clone https://github.com/josedomingues/simple-ecommerce-system.git` +2. Navigate to the project directory: `cd simple-ecommerce-system` +3. Install dependencies: `composer install` +4. Create a database and update the database configuration in the `config/database.php` file. +5. Run the database migrations: `phinx migrate` +6. Run the database seeds: `phinx seed:run` +7. Start the web server: `php -S localhost:8000 -t public` +8. Access the website: `http://localhost:8000` +## Usage +* **Home Page**: Accessible at http://localhost:8000 +* **Product Management**: Create, edit, and view products. +* **User Management**: Register, login, and manage user profiles. +* **Address Management**: Add and manage user addresses. +* **Profile Management**: Edit and view user profiles. -## Products -This is where the bulk of the database operations happens and we establish the necessary queries for each one of the operations to add, delete and update the database. -Some of the queries might be a bit complex so I did my best to explain them in the file with comments. -Other relevant aspects: The product_categories table has two foreign key constraints that reference the product and category ids. The on delete cascade rule ensures that when a product is deleted from the products table, all related entries on the product_categories table are automatically deleted. +## Directory Structure +* **/core**: Core application files including the main app, container, router, and utility functions. +* **/models**: Database models for products, users, addresses, and countries. +* **/db**: Database migrations, seeds and connection configuration. +* **/views**: HTML and PHP views for different parts of the application. +* **/partials**: Common partial views like header, navigation, and footer. +* **/products**: Views for product-related pages. +* **/users**: Views for user-related pages. +* **/profile**: Views for profile-related pages. +* **/controllers**: Controllers handling the logic for different parts of the application. +* **/web**: Available routes. +* **/public**: Publicly accessible files including the main entry point, CSS, images, and JavaScript. -## SQL Queries in `Products` Class +## Stack +* PHP 8.2 +* Git +* mysql +* Composer +* Phinx +* nginx -| Method | Query Type | SQL Query | Description | -|--------------|------------|-----------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------| -| `get($id)` | `SELECT` | ```SELECT p.*, GROUP_CONCAT(c.name) as categories FROM products p LEFT JOIN product_categories pc ON p.id = pc.product_id LEFT JOIN categories c ON pc.category_id = c.id WHERE p.id = :id GROUP BY p.id``` | Retrieves a single product with its associated categories based on the provided product ID. | -| `get()` | `SELECT` | ```SELECT p.*, GROUP_CONCAT(c.name) as categories FROM products p LEFT JOIN product_categories pc ON p.id = pc.product_id LEFT JOIN categories c ON pc.category_id = c.id GROUP BY p.id``` | Retrieves all products with their associated categories. | -| `delete($id)`| `DELETE` | ```DELETE FROM products WHERE id = :id``` | Deletes a product from the `products` table based on the provided product ID. | -| `add($name, $price, $description, $categories)` | `INSERT` | ```INSERT INTO products (name, price, description) VALUES (:name, :price, :description)``` | Adds a new product to the `products` table with the provided name, price, and description. | -| | `SELECT` | ```SELECT LAST_INSERT_ID()``` | Retrieves the ID of the newly inserted product. | -| | `SELECT` | ```SELECT id FROM categories WHERE name = :name``` | Retrieves the ID of a category based on its name. | -| | `INSERT` | ```INSERT INTO product_categories (product_id, category_id) VALUES (:product_id, :category_id)``` | Associates the new product with its categories by inserting into the `product_categories` table. | -| `update($id, $name, $price, $description, $categories)` | `UPDATE` | ```UPDATE products SET name = :name, price = :price, description = :description WHERE id = :id``` | Updates the product's name, price, and description in the `products` table based on the provided product ID. | -| | `DELETE` | ```DELETE FROM product_categories WHERE product_id = :product_id``` | Deletes existing category associations for the product in the `product_categories` table. | -| | `SELECT` | ```SELECT id FROM categories WHERE name = :name``` | Retrieves the ID of a category based on its name. | -| | `INSERT` | ```INSERT INTO product_categories (product_id, category_id) VALUES (:product_id, :category_id)``` | Re-associates the product with the provided categories by inserting into the `product_categories` table. | +## Development Environment +* WSL +* PHPStorm +* TablePlus -### Explanation -- **`get($id)`**: Fetches a single product and its categories if an ID is provided. -- **`get()`**: Fetches all products and their categories if no ID is provided. -- **`delete($id)`**: Deletes a product by its ID. -- **`add($name, $price, $description, $categories)`**: Adds a new product and associates it with categories. -- **`update($id, $name, $price, $description, $categories)`**: Updates an existing product's details and re-associates it with categories. \ No newline at end of file +## Author +[Jose Domingues](jose.domingues@sage.com) \ No newline at end of file diff --git a/Structure.save b/Structure.save index 4f62124c364a24a6e8fc4f8c656ff048817e99c5..d32c7e1d1b4453c40727b33ecf5c8b45dae8375d 100644 --- a/Structure.save +++ b/Structure.save @@ -5,11 +5,15 @@ functions.php Router.php Flash.php - Middleware.php + Mailer.php /models Product.php User.php + Address.php + Country.php /db + /migrations + /seeds Database.php /views /partials @@ -24,10 +28,19 @@ /users login.php register.php + edit.user.php + reset.password.php + show.all.php + /profile + edit.profile.php + profile.php /controllers HomeController.php ProductsController.php UserController.php + ProfileController.php + LoginController.php + AddressController.php /web routes.php /public diff --git a/composer.json b/composer.json index a506381b33f03493e05ff2ed4f07c4325b7654dd..3862ad32c2f3af8fc8ceda9902ccdfd77e6f89bd 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,8 @@ } ], "require": { - "phpmailer/phpmailer": "^6.9" + "phpmailer/phpmailer": "^6.9", + "robmorgan/phinx": "^0.16.5" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index fbc5753543c16f83b8b72c7a745b220a27880bd1..cfa88fc643c69ffe98b2d215b290b6cf0a86ec9c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,394 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "78fc7c15f25f36eabb0edb2494145fe4", + "content-hash": "c60b905fb3ca29cf0f2d8706691afc99", "packages": [ + { + "name": "cakephp/chronos", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/cakephp/chronos.git", + "reference": "786d69e1ee4b735765cbdb5521b9603e9b98d650" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/chronos/zipball/786d69e1ee4b735765cbdb5521b9603e9b98d650", + "reference": "786d69e1ee4b735765cbdb5521b9603e9b98d650", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/clock": "^1.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "cakephp/cakephp-codesniffer": "^5.0", + "phpunit/phpunit": "^10.1.0 || ^11.1.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cake\\Chronos\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "http://nesbot.com" + }, + { + "name": "The CakePHP Team", + "homepage": "https://cakephp.org" + } + ], + "description": "A simple API extension for DateTime.", + "homepage": "https://cakephp.org", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "issues": "https://github.com/cakephp/chronos/issues", + "source": "https://github.com/cakephp/chronos" + }, + "time": "2024-07-18T03:18:04+00:00" + }, + { + "name": "cakephp/core", + "version": "5.1.2", + "source": { + "type": "git", + "url": "https://github.com/cakephp/core.git", + "reference": "70f99f5df4dde8677ddcf6245654a8b937f7786b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/core/zipball/70f99f5df4dde8677ddcf6245654a8b937f7786b", + "reference": "70f99f5df4dde8677ddcf6245654a8b937f7786b", + "shasum": "" + }, + "require": { + "cakephp/utility": "^5.1", + "league/container": "^4.2", + "php": ">=8.1", + "psr/container": "^1.1 || ^2.0" + }, + "provide": { + "psr/container-implementation": "^2.0" + }, + "suggest": { + "cakephp/cache": "To use Configure::store() and restore().", + "cakephp/event": "To use PluginApplicationInterface or plugin applications.", + "league/container": "To use Container and ServiceProvider classes" + }, + "type": "library", + "autoload": { + "files": [ + "functions.php" + ], + "psr-4": { + "Cake\\Core\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/core/graphs/contributors" + } + ], + "description": "CakePHP Framework Core classes", + "homepage": "https://cakephp.org", + "keywords": [ + "cakephp", + "core", + "framework" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/cakephp/issues", + "source": "https://github.com/cakephp/core" + }, + "time": "2024-10-31T06:55:29+00:00" + }, + { + "name": "cakephp/database", + "version": "5.1.2", + "source": { + "type": "git", + "url": "https://github.com/cakephp/database.git", + "reference": "0266cba5968f089cbca8785dbae4450623a6f055" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/database/zipball/0266cba5968f089cbca8785dbae4450623a6f055", + "reference": "0266cba5968f089cbca8785dbae4450623a6f055", + "shasum": "" + }, + "require": { + "cakephp/chronos": "^3.1", + "cakephp/core": "^5.1", + "cakephp/datasource": "^5.1", + "php": ">=8.1", + "psr/log": "^3.0" + }, + "require-dev": { + "cakephp/i18n": "^5.1", + "cakephp/log": "^5.1" + }, + "suggest": { + "cakephp/i18n": "If you are using locale-aware datetime formats.", + "cakephp/log": "If you want to use query logging without providing a logger yourself." + }, + "type": "library", + "autoload": { + "psr-4": { + "Cake\\Database\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/database/graphs/contributors" + } + ], + "description": "Flexible and powerful Database abstraction library with a familiar PDO-like API", + "homepage": "https://cakephp.org", + "keywords": [ + "abstraction", + "cakephp", + "database", + "database abstraction", + "pdo" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/cakephp/issues", + "source": "https://github.com/cakephp/database" + }, + "time": "2024-11-02T13:34:55+00:00" + }, + { + "name": "cakephp/datasource", + "version": "5.1.2", + "source": { + "type": "git", + "url": "https://github.com/cakephp/datasource.git", + "reference": "9ffde854a988cadc740f419b602f9872260908a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/datasource/zipball/9ffde854a988cadc740f419b602f9872260908a8", + "reference": "9ffde854a988cadc740f419b602f9872260908a8", + "shasum": "" + }, + "require": { + "cakephp/core": "^5.1", + "php": ">=8.1", + "psr/simple-cache": "^2.0 || ^3.0" + }, + "require-dev": { + "cakephp/cache": "^5.1", + "cakephp/collection": "^5.1", + "cakephp/utility": "^5.1" + }, + "suggest": { + "cakephp/cache": "If you decide to use Query caching.", + "cakephp/collection": "If you decide to use ResultSetInterface.", + "cakephp/utility": "If you decide to use EntityTrait." + }, + "type": "library", + "autoload": { + "psr-4": { + "Cake\\Datasource\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/datasource/graphs/contributors" + } + ], + "description": "Provides connection managing and traits for Entities and Queries that can be reused for different datastores", + "homepage": "https://cakephp.org", + "keywords": [ + "cakephp", + "connection management", + "datasource", + "entity", + "query" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/cakephp/issues", + "source": "https://github.com/cakephp/datasource" + }, + "time": "2024-10-17T08:33:47+00:00" + }, + { + "name": "cakephp/utility", + "version": "5.1.2", + "source": { + "type": "git", + "url": "https://github.com/cakephp/utility.git", + "reference": "a1a503e9ef96761242eb260df0e04eb0e068e5cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/utility/zipball/a1a503e9ef96761242eb260df0e04eb0e068e5cd", + "reference": "a1a503e9ef96761242eb260df0e04eb0e068e5cd", + "shasum": "" + }, + "require": { + "cakephp/core": "^5.1", + "php": ">=8.1" + }, + "suggest": { + "ext-intl": "To use Text::transliterate() or Text::slug()", + "lib-ICU": "To use Text::transliterate() or Text::slug()" + }, + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Cake\\Utility\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/utility/graphs/contributors" + } + ], + "description": "CakePHP Utility classes such as Inflector, String, Hash, and Security", + "homepage": "https://cakephp.org", + "keywords": [ + "cakephp", + "hash", + "inflector", + "security", + "string", + "utility" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/cakephp/issues", + "source": "https://github.com/cakephp/utility" + }, + "time": "2024-11-08T14:07:41+00:00" + }, + { + "name": "league/container", + "version": "4.2.4", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/container.git", + "reference": "7ea728b013b9a156c409c6f0fc3624071b742dec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/container/zipball/7ea728b013b9a156c409c6f0fc3624071b742dec", + "reference": "7ea728b013b9a156c409c6f0fc3624071b742dec", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "psr/container": "^1.1 || ^2.0" + }, + "provide": { + "psr/container-implementation": "^1.0" + }, + "replace": { + "orno/di": "~2.0" + }, + "require-dev": { + "nette/php-generator": "^3.4", + "nikic/php-parser": "^4.10", + "phpstan/phpstan": "^0.12.47", + "phpunit/phpunit": "^8.5.17", + "roave/security-advisories": "dev-latest", + "scrutinizer/ocular": "^1.8", + "squizlabs/php_codesniffer": "^3.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev", + "dev-4.x": "4.x-dev", + "dev-3.x": "3.x-dev", + "dev-2.x": "2.x-dev", + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Container\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Phil Bennett", + "email": "mail@philbennett.co.uk", + "role": "Developer" + } + ], + "description": "A fast and intuitive dependency injection container.", + "homepage": "https://github.com/thephpleague/container", + "keywords": [ + "container", + "dependency", + "di", + "injection", + "league", + "provider", + "service" + ], + "support": { + "issues": "https://github.com/thephpleague/container/issues", + "source": "https://github.com/thephpleague/container/tree/4.2.4" + }, + "funding": [ + { + "url": "https://github.com/philipobenito", + "type": "github" + } + ], + "time": "2024-11-10T12:42:13+00:00" + }, { "name": "phpmailer/phpmailer", "version": "v6.9.2", @@ -86,6 +472,1084 @@ } ], "time": "2024-10-09T10:07:50+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "robmorgan/phinx", + "version": "0.16.5", + "source": { + "type": "git", + "url": "https://github.com/cakephp/phinx.git", + "reference": "31d837c17d6dc294d44f0b17331066548ac8c032" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/phinx/zipball/31d837c17d6dc294d44f0b17331066548ac8c032", + "reference": "31d837c17d6dc294d44f0b17331066548ac8c032", + "shasum": "" + }, + "require": { + "cakephp/database": "^5.0.2", + "composer-runtime-api": "^2.0", + "php-64bit": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/config": "^3.4|^4.0|^5.0|^6.0|^7.0", + "symfony/console": "^6.0|^7.0" + }, + "require-dev": { + "cakephp/cakephp": "^5.0.2", + "cakephp/cakephp-codesniffer": "^5.0", + "ext-json": "*", + "ext-pdo": "*", + "phpunit/phpunit": "^9.5.19", + "symfony/yaml": "^3.4|^4.0|^5.0|^6.0|^7.0" + }, + "suggest": { + "ext-json": "Install if using JSON configuration format", + "ext-pdo": "PDO extension is needed", + "symfony/yaml": "Install if using YAML configuration format" + }, + "bin": [ + "bin/phinx" + ], + "type": "library", + "autoload": { + "psr-4": { + "Phinx\\": "src/Phinx/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Rob Morgan", + "email": "robbym@gmail.com", + "homepage": "https://robmorgan.id.au", + "role": "Lead Developer" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com", + "homepage": "https://shadowhand.me", + "role": "Developer" + }, + { + "name": "Richard Quadling", + "email": "rquadling@gmail.com", + "role": "Developer" + }, + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/phinx/graphs/contributors", + "role": "Developer" + } + ], + "description": "Phinx makes it ridiculously easy to manage the database migrations for your PHP app.", + "homepage": "https://phinx.org", + "keywords": [ + "database", + "database migrations", + "db", + "migrations", + "phinx" + ], + "support": { + "issues": "https://github.com/cakephp/phinx/issues", + "source": "https://github.com/cakephp/phinx/tree/0.16.5" + }, + "time": "2024-10-03T15:41:04+00:00" + }, + { + "name": "symfony/config", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "bcd3c4adf0144dee5011bb35454728c38adec055" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/bcd3c4adf0144dee5011bb35454728c38adec055", + "reference": "bcd3c4adf0144dee5011bb35454728c38adec055", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/filesystem": "^7.1", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/finder": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "require-dev": { + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-04T11:36:24+00:00" + }, + { + "name": "symfony/console", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "23c8aae6d764e2bae02d2a99f7532a7f6ed619cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/23c8aae6d764e2bae02d2a99f7532a7f6ed619cf", + "reference": "23c8aae6d764e2bae02d2a99f7532a7f6ed619cf", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^6.4|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-06T14:24:19+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-25T15:15:23+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/string", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82", + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-13T13:31:26+00:00" } ], "packages-dev": [], diff --git a/composer.phar b/composer.phar new file mode 100755 index 0000000000000000000000000000000000000000..f5d9d1ab15e98b27a5fd305404ba6e93a548287f Binary files /dev/null and b/composer.phar differ diff --git a/controllers/AddressController.php b/controllers/AddressController.php new file mode 100644 index 0000000000000000000000000000000000000000..239e7ed90870f218aae159b57f8bfb303a8ac87b --- /dev/null +++ b/controllers/AddressController.php @@ -0,0 +1,114 @@ +<?php + +namespace controllers; + +use core\Flash; +use models\Address; +use models\Country; + +class AddressController +{ + + private $addresses; + private $country; + + public function __construct() + { + $this->addresses = new Address(); + $this->country = new Country(); + } + + public function create(): void + { + $countries = $this->country->getCountries(); + $user_id = filter_input(INPUT_GET, 'user_id', FILTER_SANITIZE_NUMBER_INT); + view('address/create.php', ['user_id' => $user_id, 'countries' => $countries]); + } + + public function destroy(): void + { + $id = filter_input(INPUT_POST, 'addressId', FILTER_SANITIZE_NUMBER_INT); + $user_id = filter_input(INPUT_POST, 'user_id', FILTER_SANITIZE_NUMBER_INT); + if ($this->addresses->getMainAddress($user_id)['id'] == $id) { + Flash::set('error', 'Please set another address as main before deleting'); + header('Location: /address/show?user_id=' . $user_id); + exit; + } + $this->addresses->deleteAddress($id); + Flash::set('success', 'Address deleted successfully!'); + + header('Location: /address/show?user_id=' . $user_id); + exit; + } + + public function store(): void + { + $street = filter_input(INPUT_POST, 'street', FILTER_SANITIZE_FULL_SPECIAL_CHARS); + $city = filter_input(INPUT_POST, 'city', FILTER_SANITIZE_FULL_SPECIAL_CHARS); + $country = filter_input(INPUT_POST, 'country', FILTER_SANITIZE_FULL_SPECIAL_CHARS); + $zipcode = filter_input(INPUT_POST, 'zipcode', FILTER_SANITIZE_FULL_SPECIAL_CHARS); + $district = filter_input(INPUT_POST, 'district', FILTER_SANITIZE_FULL_SPECIAL_CHARS); + $user_id = filter_input(INPUT_POST, 'user_id', FILTER_SANITIZE_NUMBER_INT); + $main = filter_input(INPUT_POST, 'main', FILTER_VALIDATE_BOOLEAN); + $inputs = [ + 'street' => $street, + 'city' => $city, + 'country' => $country, + 'zipcode' => $zipcode, + 'district' => $district + ]; + + $validationErrors = validateInput($inputs); + if (!empty($validationErrors)) { + foreach ($validationErrors as $error) { + Flash::set('error', $error); + } + header('Location: /user/edit?id=' . $user_id); // Redirect back to the edit form + exit; + } + + $this->addresses->addAddressSetMain($street, $city, $country, $zipcode, $district, $user_id, $main); + + Flash::set('success', 'Address added successfully!'); + header('Location: /address/show?user_id=' . $user_id); + exit; + } + + public function edit() + { + $countries = $this->country->getCountries(); + $id = filter_input(INPUT_GET, 'addressId', FILTER_SANITIZE_NUMBER_INT); + $user_id = filter_input(INPUT_GET, 'user_id', FILTER_SANITIZE_NUMBER_INT); + $address = $this->addresses->getAddress($id); + view('address/edit.php', ['address' => $address, 'user_id' => $user_id, 'countries' => $countries]); + } + + public function update(): void + { + $addressId = filter_input(INPUT_POST, 'addressId', FILTER_SANITIZE_NUMBER_INT); + $street = filter_input(INPUT_POST, 'street', FILTER_SANITIZE_FULL_SPECIAL_CHARS); + $city = filter_input(INPUT_POST, 'city', FILTER_SANITIZE_FULL_SPECIAL_CHARS); + $country = filter_input(INPUT_POST, 'country', FILTER_SANITIZE_FULL_SPECIAL_CHARS); + $zipcode = filter_input(INPUT_POST, 'zipcode', FILTER_SANITIZE_FULL_SPECIAL_CHARS); + $district = filter_input(INPUT_POST, 'district', FILTER_SANITIZE_FULL_SPECIAL_CHARS); + $user_id = filter_input(INPUT_POST, 'user_id', FILTER_SANITIZE_NUMBER_INT); + $mainAddress = filter_input(INPUT_POST, 'main', FILTER_VALIDATE_BOOLEAN); + if ($mainAddress == 1) { + $this->addresses->setMainAddress($user_id, $addressId); + } + + $this->addresses->updateAddress($addressId, $street, $city, $country, $zipcode, $district, $user_id); + Flash::set('success', 'Address updated successfully!'); + header('Location: /address/show?user_id=' . $user_id); + exit; + } + + public function show(): void + { + $user_id = filter_input(INPUT_GET, 'user_id', FILTER_SANITIZE_NUMBER_INT); + $addresses = $this->addresses->getAddresses($user_id); + $user = $_SESSION['user']; + view('address/show.php', ['addresses' => $addresses, 'user' => $user, 'user_id' => $user_id]); + } + +} \ No newline at end of file diff --git a/controllers/LoginController.php b/controllers/LoginController.php index e11c457d15f920c16d7fe09c60260da0c8b7505c..71565b55c76031e54e42b3a8d22a891cb7a8ee12 100644 --- a/controllers/LoginController.php +++ b/controllers/LoginController.php @@ -20,10 +20,6 @@ class LoginController public function showLogin(): void { - $loggedOut = filter_input(INPUT_GET, 'loggedOut', FILTER_SANITIZE_SPECIAL_CHARS); - if ($loggedOut === 'true') { - Flash::set('success', 'You logged out successfully!'); - } view("users/login.php"); } @@ -46,20 +42,24 @@ class LoginController { $email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL); $password = filter_input(INPUT_POST, 'password', FILTER_SANITIZE_FULL_SPECIAL_CHARS); - $user = $this->users->getByEmail($email); - if (empty($email)) { - Flash::set('error', "Empty Login field"); - header('Location: /login'); - exit; - } - if (empty($password)) { - Flash::set('error', "Empty Password field"); - header('Location: /login'); - exit; + + $inputs = [ + 'email' => $email, + 'password' => $password + ]; + + $validationErrors = validateInput($inputs); + + if (!empty($validationErrors)) { + foreach ($validationErrors as $error) { + Flash::set('error', $error); + header('Location: /login'); + exit; + } } - if ($this->users->login($password, $user)) { + if ($this->users->login($email, $password)) { Flash::set('success', 'You Logged in successfully'); header('Location: /products/index'); } else { @@ -72,7 +72,8 @@ class LoginController public function logout() { $this->users->logout(); - header('Location: /login?loggedOut=true'); + Flash::set('success', 'Logged out successfully!'); + header('Location: /login'); exit; } @@ -82,6 +83,11 @@ class LoginController $email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL); $password = filter_input(INPUT_POST, 'password', FILTER_SANITIZE_FULL_SPECIAL_CHARS); $confirm_password = filter_input(INPUT_POST, 'confirm_password', FILTER_SANITIZE_FULL_SPECIAL_CHARS); + $street = filter_input(INPUT_POST, 'street', FILTER_SANITIZE_FULL_SPECIAL_CHARS); + $country = filter_input(INPUT_POST, 'country', FILTER_SANITIZE_FULL_SPECIAL_CHARS); + $district = filter_input(INPUT_POST, 'district', FILTER_SANITIZE_FULL_SPECIAL_CHARS); + $city = filter_input(INPUT_POST, 'city', FILTER_SANITIZE_FULL_SPECIAL_CHARS); + $zipcode = filter_input(INPUT_POST, 'zipcode', FILTER_SANITIZE_FULL_SPECIAL_CHARS); $is_admin = filter_input(INPUT_POST, 'is_admin', FILTER_SANITIZE_NUMBER_INT); $inputs = [ @@ -89,7 +95,11 @@ class LoginController 'email' => $email, 'password' => $password, 'confirm_password' => $confirm_password, - 'is_admin' => $is_admin + 'street' => $street, + 'country' => $country, + 'district' => $district, + 'city' => $city, + 'zipcode' => $zipcode, ]; $validationErrors = validateInput($inputs); @@ -102,39 +112,31 @@ class LoginController } } - - if (!validateEmail($email)) { - Flash::set('error', 'The email has to follow the .com convention!'); - header('Location: /register'); - exit; - } - - // Check if email already exists - if ($this->users->getByEmail($email)) { - Flash::set('error', 'Email already in use. Please follow this <a href="/login">link </a>to login!'); + if ($this->users->checkByEmail($email)) { header('Location: /register'); + Flash::set('error', 'Email already in use!'); exit; } if (!validatePassword($password)) { + header('Location: /register'); Flash::set( 'error', - 'Invalid Password! Your Password must be at least 12 characters long and include at least one uppercase letter, one lowercase letter, one number, and one special character.' + 'Password must be at least 12 characters long and include at least one uppercase letter, one lowercase letter, one number, and one special character.' ); - header('Location: /register'); exit; } if (!($password === $confirm_password)) { - Flash::set('error', 'The Password and the Confirm Password fields must match!'); header('Location: /register'); + Flash::set('error', 'The Password and the Confirm Password fields must match!'); exit; } // Register the user - $this->users->register($name, $email, $password, $address, $is_admin); - Flash::set('success', 'Registered successfully!'); + $this->users->register($name, $email, $password, $street, $zipcode, $country, $city, $district, $is_admin); header('Location: /login'); + Flash::set('success', 'Registered successfully!'); exit; } @@ -143,25 +145,21 @@ class LoginController $email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL); if (empty($email)) { - Flash::set('error', 'Empty email field!'); header('Location: /recover-password'); + Flash::set('error', 'Empty email field!'); exit; } $token = bin2hex(random_bytes(32)); // 64 characters long token $expiry = date('Y-m-d H:i:s', strtotime('+1 hour')); // Set token expiry time - if($this->users->storeToken($email, $token, $expiry)) { - $resetLink = "127.0.0.1:8080/reset-password?token=" . $token; + $this->users->storeToken($email, $token, $expiry); + $resetLink = DOMAIN_PATH . "reset-password?token=" . $token; - $this->mail->send($email, $resetLink); - Flash::set( - 'success', - "An email was sent to the provided email address. Please follow the instructions in the email to reset your password." - ); - header('Location: /home/index'); - exit; - } + $this->mail->send($email, $resetLink); + Flash::set('success', "Follow the link in your email to reset the password!"); + header('Location: /home/index'); + exit; } public function resetPassword(): void @@ -170,13 +168,17 @@ class LoginController $confirm_password = filter_input(INPUT_POST, 'confirm_password', FILTER_SANITIZE_FULL_SPECIAL_CHARS); $token = filter_input(INPUT_POST, 'token', FILTER_SANITIZE_FULL_SPECIAL_CHARS); - if (empty($password)) { - Flash::set('error', "Empty password field! "); - header('Location: /reset-password'); - exit; - } - if (empty($confirm_password)) { - Flash::set('error', "Empty Retype Password field! "); + $inputs = [ + 'password' => $password, + 'confirm_password' => $confirm_password, + ]; + + $validationErrors = validateInput($inputs); + + if (!empty($validationErrors)) { + foreach ($validationErrors as $error) { + Flash::set('error', $error); + } header('Location: /reset-password'); exit; } @@ -196,15 +198,15 @@ class LoginController exit; } - if (!$this->users->updatePassword($token, $password)) { + if ($this->users->updatePassword($token, $password)) { + Flash::set('success', "The password was changed successfully!"); + header('Location: /login'); + } else { Flash::set('error', "Your validation token has expired!"); header('Location: /recover-password'); - exit; } - Flash::set('success', "The password was changed successfully!"); - header('Location: /login'); exit; - } -} \ No newline at end of file + +} diff --git a/controllers/ProfileController.php b/controllers/ProfileController.php index 10ebd01df88357d5875822eac064631992a98119..62e2d7a3469a00665006b9baa80023b72815216e 100644 --- a/controllers/ProfileController.php +++ b/controllers/ProfileController.php @@ -4,39 +4,49 @@ namespace controllers; use core\Flash; use models\User; +use models\Address; class ProfileController { private $users; + private $addresses; public function __construct() { $this->users = new User(); + $this->addresses = new Address(); } public function show(): void { - view('profile/profile.php'); + $user = $_SESSION['user'] ?? null; + if (!$user) { + header('Location: /login'); + } + $address = $this->addresses->getMainAddress($user['id']); + view('profile/profile.php', ['user' => $user, 'address' => $address]); } public function edit(): void { - view('profile/edit.profile.php'); + $user = $_SESSION['user'] ?? null; + if (!$user) { + header('Location: /login'); + } + $address = $this->addresses->getAddresses($user['id']); + view('profile/edit.profile.php', ['user' => $user, 'address' => $address]); } public function update(): void { $name = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_FULL_SPECIAL_CHARS); $email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL); - $address = filter_input(INPUT_POST, 'address', FILTER_SANITIZE_FULL_SPECIAL_CHARS); $is_admin = filter_input(INPUT_POST, 'is_admin', FILTER_VALIDATE_BOOLEAN); $inputs = [ 'name' => $name, 'email' => $email, - 'address' => $address, - 'is_admin' => $is_admin, ]; $validationErrors = validateInput($inputs); @@ -51,12 +61,11 @@ class ProfileController // Update the user information in the database - $this->users->update($_SESSION['user']['id'], $name, $email, $address, $is_admin); + $this->users->update($_SESSION['user']['id'], $name, $email, $is_admin); // Update the session data with the new information $_SESSION['user']['name'] = $name; $_SESSION['user']['email'] = $email; - $_SESSION['user']['address'] = $address; $_SESSION['user']['is_admin'] = $is_admin; // Update session for admin status Flash::set('success', 'Profile edited successfully!'); diff --git a/controllers/UserController.php b/controllers/UserController.php index 0e792e3cadf6e47eaa166e6247f301e5ad96986d..c2fdde7e592f6349b48a23f23717067e38413159 100644 --- a/controllers/UserController.php +++ b/controllers/UserController.php @@ -21,10 +21,10 @@ class UserController public function index(): void { $users = $this->users->getAll(); - view('users/show.all.php', ['users' => $users]); // Pass users to the view - + view('users/show.all.php', ['users' => $users]); } + public function destroy(): void { $id = filter_input(INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT); @@ -46,40 +46,33 @@ class UserController public function edit(): void { $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); - $selectedUser = $this->users->get($id); - if ($selectedUser['is_admin']) { - Flash::set('error', "You can't update an admin account!"); - - header('Location: /user/index'); // Redirect back to the user management page - exit; - } else { - view('users/edit.user.php', ['selectedUser' => $selectedUser]); - } + $user = $this->users->get($id); + view('users/edit.user.php', ['user' => $user]); } public function update(): void { $id = filter_input(INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT); $name = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_FULL_SPECIAL_CHARS); - $address = filter_input(INPUT_POST, 'address', FILTER_SANITIZE_FULL_SPECIAL_CHARS); $is_admin = filter_input(INPUT_POST, 'is_admin', FILTER_VALIDATE_BOOLEAN); $email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL); + + $inputs = [ 'name' => $name, - 'address' => $address, - 'is_admin' => $is_admin ]; + $validationErrors = validateInput($inputs); if (!empty($validationErrors)) { foreach ($validationErrors as $error) { Flash::set('error', $error); } - header('Location: /user/edit?id=' . $id); + header('Location: /user/edit?id=' . $id); // Redirect back to the edit form exit; } - $this->users->update($id, $name, $email, $address, $is_admin); + $this->users->update($id, $name, $email, $is_admin); Flash::set('success', 'User updated successfully!'); header('Location: /user/index'); diff --git a/core/Mailer.php b/core/Mailer.php index 7d30d7ef83db82b1525555fdf1a7ceda7936f900..b9bd208686ee5c6d60a778b96bf6175c811deabf 100644 --- a/core/Mailer.php +++ b/core/Mailer.php @@ -6,7 +6,7 @@ use PHPMailer\PHPMailer\PHPMailer; class Mailer { - private $mail; + private $mail; public function __construct() { @@ -21,11 +21,11 @@ class Mailer $this->mail->setFrom('simple-ecommerce-system@example.com', 'Mailer'); $this->mail->isHTML(true); $this->mail->Subject = 'Password Recovery'; - } - public function send($to, $resetLink) :void{ - $this->mail->Body = 'Use this link to reset your password: '. $resetLink; + public function send($to, $resetLink): void + { + $this->mail->Body = 'Use this link to reset your password: ' . $resetLink; $this->mail->addAddress($to, 'User'); // Add a recipient $this->mail->send(); } diff --git a/core/Router.php b/core/Router.php index 9e7afd64868998aca983e9e8915ebeca9750e949..c2b697e12f794c52a5826c30fa700174a0950da8 100644 --- a/core/Router.php +++ b/core/Router.php @@ -1,11 +1,12 @@ <?php namespace core; + class Router { protected $routes = []; - public function get( string $uri, string $controller, string $method): self + public function get(string $uri, string $controller, string $method): self { $this->routes[] = [ 'uri' => $uri, @@ -17,7 +18,7 @@ class Router return $this; } - public function post( string $uri, string $controller, string $method): self + public function post(string $uri, string $controller, string $method): self { $this->routes[] = [ 'uri' => $uri, @@ -30,21 +31,21 @@ class Router return $this; } - public function only(string $key) :self + public function only(string $key): self { $this->routes[array_key_last($this->routes)]['middleware'] = $key; return $this; } - public function route(string $uri, string $method) : mixed + public function route(string $uri, string $method): mixed { $parsedUrl = parse_url($uri); $path = $parsedUrl['path']; foreach ($this->routes as $route) { if ($route['uri'] === $path && $route['httpMethod'] === $method) { - $middlewares = (array) $route['middleware']; // Ensure it's an array + $middlewares = (array)$route['middleware']; // Ensure it's an array foreach ($middlewares as $middleware) { if ($middleware === 'guest') { diff --git a/core/functions.php b/core/functions.php index ae4e67ce516dba293ee0abb59f130c0c62210e7b..95cc820deefc78a904d3237a09c96d05424c3697 100644 --- a/core/functions.php +++ b/core/functions.php @@ -13,27 +13,23 @@ function view(string $path, array $attributes = []): void require base_path('views/' . $path); } -function validatePassword($password){ - if(strlen($password) < 12 || !preg_match('/[A-Z]/', $password) || !preg_match('/[a-z]/', $password) || !preg_match('/[0-9]/', $password) || !preg_match('/[\W_]/', $password)){ +function validatePassword($password) +{ + if (strlen($password) < 12 || !preg_match('/[A-Z]/', $password) || !preg_match('/[a-z]/', $password) || !preg_match( + '/[0-9]/', + $password + ) || !preg_match('/[\W_]/', $password)) { return false; } return true; } -function validateEmail(string $email): bool -{ - $pattern = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[cC][oO][mM]$/'; - if (preg_match($pattern, $email)) { - return true; - } else { - return false; - } -} -function validateInput(array $inputs): array{ +function validateInput(array $inputs): array +{ $errors = []; - foreach ($inputs as $key =>$input){ - if (empty($input)){ + foreach ($inputs as $key => $input) { + if (empty($input)) { $errors[] = " The " . ucfirst($key) . " field is required!"; } } @@ -41,8 +37,15 @@ function validateInput(array $inputs): array{ } - - +function validateEmail(string $email): bool +{ + $pattern = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[cC][oO][mM]$/'; + if (preg_match($pattern, $email)) { + return true; + } else { + return false; + } +} diff --git a/db/migrations/20241203110000_create_table_users.php b/db/migrations/20241203110000_create_table_users.php new file mode 100644 index 0000000000000000000000000000000000000000..3f892f79aeab59816cbb735c6071432d37a2e9b6 --- /dev/null +++ b/db/migrations/20241203110000_create_table_users.php @@ -0,0 +1,42 @@ +<?php + +declare(strict_types=1); + +use Phinx\Migration\AbstractMigration; + +final class CreateTableUsers extends AbstractMigration +{ + /** + * Change Method. + * + * Write your reversible migrations using this method. + * + * More information on writing migrations is available here: + * https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method + * + * Remember to call "create()" or "update()" and NOT "save()" when working + * with the Table class. + */ + public function change(): void + { + $table = $this->table('users'); + $table ->addColumn('name', 'string', ['limit' => 100, 'null' => false]) + ->addColumn('email', 'string', ['limit' => 255, 'null' => false]) + ->addColumn('password', 'string', ['limit' => 255, 'null' => false]) + ->addColumn('is_admin', 'boolean', ['null' => false, 'default' => false]) + ->addIndex(['email'], ['unique' => true]) + ->create(); + if ($this->isMigratingUp()) { + $table->insert( + [ + [ + 'name' => 'António', + 'email' => 'antonio@sage.com', + 'password' => password_hash('Antonioo1607!', PASSWORD_BCRYPT), + 'is_admin' => '1' + ] + ] + )->save(); + } + } +} diff --git a/db/migrations/20241203120000_create_table_address.php b/db/migrations/20241203120000_create_table_address.php new file mode 100644 index 0000000000000000000000000000000000000000..43e60f0df6c88cdd5bcd9c7a9b3d81b693f9222c --- /dev/null +++ b/db/migrations/20241203120000_create_table_address.php @@ -0,0 +1,49 @@ +<?php + +declare(strict_types=1); + +use Phinx\Migration\AbstractMigration; + +final class CreateTableAddress extends AbstractMigration +{ + /** + * Change Method. + * + * Write your reversible migrations using this method. + * + * More information on writing migrations is available here: + * https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method + * + * Remember to call "create()" or "update()" and NOT "save()" when working + * with the Table class. + */ + public function change(): void + { + $table = $this->table('address'); + $table->addColumn('street', 'string', ['limit' => 255, 'null' => true]) + ->addColumn('zipcode', 'string', ['limit' => 255, 'null' => true]) + ->addColumn('city', 'string', ['limit' => 255, 'null' => true]) + ->addColumn('district', 'string', ['limit' => 255, 'null' => true]) + ->addColumn('country', 'string', ['limit' => 255, 'null' => true]) + ->addColumn('userid', 'integer', ['signed' => false, 'null' => false]) + ->addColumn('main', 'boolean', ['null' => true, 'default' => false]) + ->addForeignKey('userid', 'users', 'id', ['delete'=> 'CASCADE', 'update'=> 'NO_ACTION']) + ->create(); + + if ($this->isMigratingUp()) { + $table->insert( + [ + [ + 'street' => 'Rua da Capela, n49', + 'zipcode' => '4410-197, Sao Felix da Marinha', + 'city' => 'Vila Nova de Gaia', + 'district' => 'Porto', + 'country' => 'Portugal', + 'userid' => '1', + 'main' => '1' + ] + ] + )->save(); + } + } +} \ No newline at end of file diff --git a/db/migrations/20241203130000_create_table_products.php b/db/migrations/20241203130000_create_table_products.php new file mode 100644 index 0000000000000000000000000000000000000000..e540b87b5dee7db7dede372980b83cbfa8fb424b --- /dev/null +++ b/db/migrations/20241203130000_create_table_products.php @@ -0,0 +1,28 @@ +<?php + +declare(strict_types=1); + +use Phinx\Migration\AbstractMigration; + +final class CreateTableProducts extends AbstractMigration +{ + /** + * Change Method. + * + * Write your reversible migrations using this method. + * + * More information on writing migrations is available here: + * https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method + * + * Remember to call "create()" or "update()" and NOT "save()" when working + * with the Table class. + */ + public function change(): void + { + $table = $this->table('products'); + $table->addColumn('name', 'string', ['limit' => 255, 'null' => false]) + ->addColumn('price', 'decimal', ['precision' => 10, 'scale' => 2, 'null' => false]) + ->addColumn('description', 'text', ['null' => true]) + ->create(); + } +} diff --git a/db/migrations/20241203140000_create_table_categories.php b/db/migrations/20241203140000_create_table_categories.php new file mode 100644 index 0000000000000000000000000000000000000000..5ca1f75bd8aedab470d96c90e6c2141f21221edc --- /dev/null +++ b/db/migrations/20241203140000_create_table_categories.php @@ -0,0 +1,26 @@ +<?php + +declare(strict_types=1); + +use Phinx\Migration\AbstractMigration; + +final class CreateTableCategories extends AbstractMigration +{ + /** + * Change Method. + * + * Write your reversible migrations using this method. + * + * More information on writing migrations is available here: + * https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method + * + * Remember to call "create()" or "update()" and NOT "save()" when working + * with the Table class. + */ + public function change(): void + { + $table = $this->table('categories'); + $table->addColumn('name', 'string', ['limit' => 255, 'null' => false]) + ->create(); + } +} diff --git a/db/migrations/20241203150000_create_table_tokens.php b/db/migrations/20241203150000_create_table_tokens.php new file mode 100644 index 0000000000000000000000000000000000000000..f471913cc653d355c89e7cde3887dec10f0e3855 --- /dev/null +++ b/db/migrations/20241203150000_create_table_tokens.php @@ -0,0 +1,29 @@ +<?php + +declare(strict_types=1); + +use Phinx\Migration\AbstractMigration; + +final class CreateTableTokens extends AbstractMigration +{ + /** + * Change Method. + * + * Write your reversible migrations using this method. + * + * More information on writing migrations is available here: + * https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method + * + * Remember to call "create()" or "update()" and NOT "save()" when working + * with the Table class. + */ + public function change(): void + { + $table = $this->table('tokens'); + $table->addColumn('user_id', 'integer', ['signed' => false, 'null' => false]) + ->addColumn('token', 'string', ['limit' => 64, 'null' => false]) + ->addColumn('expires_at', 'datetime', ['null' => false]) + ->addForeignKey('user_id', 'users', 'id', ['delete'=> 'CASCADE', 'update'=> 'NO_ACTION']) + ->create(); + } +} diff --git a/db/migrations/20241203160000_create_table_product_categories.php b/db/migrations/20241203160000_create_table_product_categories.php new file mode 100644 index 0000000000000000000000000000000000000000..b5d676d4f41b4f0069ee7e10bd819a0ad84b2355 --- /dev/null +++ b/db/migrations/20241203160000_create_table_product_categories.php @@ -0,0 +1,30 @@ +<?php + +declare(strict_types=1); + +use Phinx\Migration\AbstractMigration; + +final class CreateTableProductCategories extends AbstractMigration +{ + /** + * Change Method. + * + * Write your reversible migrations using this method. + * + * More information on writing migrations is available here: + * https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method + * + * Remember to call "create()" or "update()" and NOT "save()" when working + * with the Table class. + */ + public function change(): void + { + $table = $this->table('product_categories', ['id' => false, 'primary_key' => ['product_id', 'category_id']]); + $table->addColumn('product_id', 'integer', ['signed' => false, 'null' => false]) + ->addColumn('category_id', 'integer', ['signed' => false, 'null' => false]) + ->addIndex(['category_id']) + ->addForeignKey('product_id', 'products', 'id', ['delete'=> 'CASCADE', 'update'=> 'NO_ACTION']) + ->addForeignKey('category_id', 'categories', 'id', ['delete'=> 'CASCADE', 'update'=> 'NO_ACTION']) + ->create(); + } +} diff --git a/db/migrations/20241209170000_create_countries_table.php b/db/migrations/20241209170000_create_countries_table.php new file mode 100644 index 0000000000000000000000000000000000000000..e72a66d25bab02016697d246dbb36f244d42390a --- /dev/null +++ b/db/migrations/20241209170000_create_countries_table.php @@ -0,0 +1,28 @@ +<?php + +declare(strict_types=1); + +use Phinx\Migration\AbstractMigration; + +final class CreateCountriesTable extends AbstractMigration +{ + /** + * Change Method. + * + * Write your reversible migrations using this method. + * + * More information on writing migrations is available here: + * https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method + * + * Remember to call "create()" or "update()" and NOT "save()" when working + * with the Table class. + */ + public function change(): void + { + $table = $this->table('countries'); + $table->addColumn('name', 'string', ['limit' => 255]) + ->addColumn('value', 'string', ['limit' => 255]) + ->create(); + + } +} diff --git a/db/seeds/CategoriesSeeder.php b/db/seeds/CategoriesSeeder.php new file mode 100644 index 0000000000000000000000000000000000000000..596a60414c3c9faf3d6947f9f987a077b15e1fdd --- /dev/null +++ b/db/seeds/CategoriesSeeder.php @@ -0,0 +1,29 @@ +<?php + +declare(strict_types=1); + +use Phinx\Seed\AbstractSeed; + +class CategoriesSeeder extends AbstractSeed +{ + /** + * Run Method. + * + * Write your database seeder using this method. + * + * More information on writing seeders is available here: + * https://book.cakephp.org/phinx/0/en/seeding.html + */ + public function run(): void + { + $data = [ + ['name' => 'Home'], + ['name' => 'Electronics'], + ['name' => 'Books'], + ['name' => 'Clothing'], + ]; + $categories = $this->table('categories'); + $categories->insert($data)->save(); + + } +} diff --git a/db/seeds/CountrySeeder.php b/db/seeds/CountrySeeder.php new file mode 100644 index 0000000000000000000000000000000000000000..5118a24da511a09f01800b3ec0ca0e135ed1d9dc --- /dev/null +++ b/db/seeds/CountrySeeder.php @@ -0,0 +1,256 @@ +<?php + +declare(strict_types=1); + +use Phinx\Seed\AbstractSeed; + +class CountrySeeder extends AbstractSeed +{ + /** + * Run Method. + * + * Write your database seeder using this method. + * + * More information on writing seeders is available here: + * https://book.cakephp.org/phinx/0/en/seeding.html + */ + public function run(): void + { + $data = [ + ['name' => 'Portugal', 'value' => 'Portugal'], + ['name' => 'Afghanistan', 'value' => 'Afghanistan'], + ['name' => 'Albania', 'value' => 'Albania'], + ['name' => 'Algeria', 'value' => 'Algeria'], + ['name' => 'American Samoa', 'value' => 'American Samoa'], + ['name' => 'Andorra', 'value' => 'Andorra'], + ['name' => 'Angola', 'value' => 'Angola'], + ['name' => 'Anguilla', 'value' => 'Anguilla'], + ['name' => 'Antarctica', 'value' => 'Antarctica'], + ['name' => 'Antigua and Barbuda', 'value' => 'Antigua and Barbuda'], + ['name' => 'Argentina', 'value' => 'Argentina'], + ['name' => 'Armenia', 'value' => 'Armenia'], + ['name' => 'Aruba', 'value' => 'Aruba'], + ['name' => 'Australia', 'value' => 'Australia'], + ['name' => 'Austria', 'value' => 'Austria'], + ['name' => 'Azerbaijan', 'value' => 'Azerbaijan'], + ['name' => 'Bahamas', 'value' => 'Bahamas'], + ['name' => 'Bahrain', 'value' => 'Bahrain'], + ['name' => 'Bangladesh', 'value' => 'Bangladesh'], + ['name' => 'Barbados', 'value' => 'Barbados'], + ['name' => 'Belarus', 'value' => 'Belarus'], + ['name' => 'Belgium', 'value' => 'Belgium'], + ['name' => 'Belize', 'value' => 'Belize'], + ['name' => 'Benin', 'value' => 'Benin'], + ['name' => 'Bermuda', 'value' => 'Bermuda'], + ['name' => 'Bhutan', 'value' => 'Bhutan'], + ['name' => 'Bolivia', 'value' => 'Bolivia'], + ['name' => 'Bosnia and Herzegowina', 'value' => 'Bosnia and Herzegowina'], + ['name' => 'Botswana', 'value' => 'Botswana'], + ['name' => 'Bouvet Island', 'value' => 'Bouvet Island'], + ['name' => 'Brazil', 'value' => 'Brazil'], + ['name' => 'British Indian Ocean Territory', 'value' => 'British Indian Ocean Territory'], + ['name' => 'Brunei Darussalam', 'value' => 'Brunei Darussalam'], + ['name' => 'Bulgaria', 'value' => 'Bulgaria'], + ['name' => 'Burkina Faso', 'value' => 'Burkina Faso'], + ['name' => 'Burundi', 'value' => 'Burundi'], + ['name' => 'Cambodia', 'value' => 'Cambodia'], + ['name' => 'Cameroon', 'value' => 'Cameroon'], + ['name' => 'Canada', 'value' => 'Canada'], + ['name' => 'Cape Verde', 'value' => 'Cape Verde'], + ['name' => 'Cayman Islands', 'value' => 'Cayman Islands'], + ['name' => 'Central African Republic', 'value' => 'Central African Republic'], + ['name' => 'Chad', 'value' => 'Chad'], + ['name' => 'Chile', 'value' => 'Chile'], + ['name' => 'China', 'value' => 'China'], + ['name' => 'Christmas Island', 'value' => 'Christmas Island'], + ['name' => 'Cocos (Keeling) Islands', 'value' => 'Cocos (Keeling) Islands'], + ['name' => 'Colombia', 'value' => 'Colombia'], + ['name' => 'Comoros', 'value' => 'Comoros'], + ['name' => 'Congo', 'value' => 'Congo'], + ['name' => 'Congo, the Democratic Republic of the'], + ['name' => 'Cook Islands', 'value' => 'Cook Islands'], + ['name' => 'Costa Rica', 'value' => 'Costa Rica'], + ['name' => 'Cote d\'Ivoire', 'value' => 'Cote d\'Ivoire'], + ['name' => 'Croatia', 'value' => 'Croatia'], + ['name' => 'Cuba', 'value' => 'Cuba'], + ['name' => 'Cyprus', 'value' => 'Cyprus'], + ['name' => 'Czech Republic', 'value' => 'Czech Republic'], + ['name' => 'Denmark', 'value' => 'Denmark'], + ['name' => 'Djibouti', 'value' => 'Djibouti'], + ['name' => 'Dominica', 'value' => 'Dominica'], + ['name' => 'Dominican Republic', 'value' => 'Dominican Republic'], + ['name' => 'East Timor', 'value' => 'East Timor'], + ['name' => 'Ecuador', 'value' => 'Ecuador'], + ['name' => 'Egypt', 'value' => 'Egypt'], + ['name' => 'El Salvador', 'value' => 'El Salvador'], + ['name' => 'Equatorial Guinea', 'value' => 'Equatorial Guinea'], + ['name' => 'Eritrea', 'value' => 'Eritrea'], + ['name' => 'Estonia', 'value' => 'Estonia'], + ['name' => 'Ethiopia', 'value' => 'Ethiopia'], + ['name' => 'Falkland Islands', 'value' => 'Falkland Islands'], + ['name' => 'Faroe Islands', 'value' => 'Faroe Islands'], + ['name' => 'Fiji', 'value' => 'Fiji'], + ['name' => 'Finland', 'value' => 'Finland'], + ['name' => 'France', 'value' => 'France'], + ['name' => 'France, Metropolitan', 'value' => 'France, Metropolitan'], + ['name' => 'French Guiana', 'value' => 'French Guiana'], + ['name' => 'French Polynesia', 'value' => 'French Polynesia'], + ['name' => 'French Southern Territories', 'value' => 'French Southern Territories'], + ['name' => 'Gabon', 'value' => 'Gabon'], + ['name' => 'Gambia', 'value' => 'Gambia'], + ['name' => 'Georgia', 'value' => 'Georgia'], + ['name' => 'Germany', 'value' => 'Germany'], + ['name' => 'Greece', 'value' => 'Greece'], + ['name' => 'Greenland', 'value' => 'Greenland'], + ['name' => 'Grenada', 'value' => 'Grenada'], + ['name' => 'Guadeloupe', 'value' => 'Guadeloupe'], + ['name' => 'Guam', 'value' => 'Guam'], + ['name' => 'Guatemala', 'value' => 'Guatemala'], + ['name' => 'Guinea', 'value' => 'Guinea'], + ['name' => 'Guinea-Bissau', 'value' => 'Guinea-Bissau'], + ['name' => 'Guyana', 'value' => 'Guyana'], + ['name' => 'Haiti', 'value' => 'Haiti'], + ['name' => 'Heard and McDonald Islands', 'value' => 'Heard and McDonald Islands'], + ['name' => 'Holy See (Vatican City State)', 'value' => 'Holy See (Vatican City State)'], + ['name' => 'Honduras', 'value' => 'Honduras'], + ['name' => 'Hong Kong', 'value' => 'Hong Kong'], + ['name' => 'Hungary', 'value' => 'Hungary'], + ['name' => 'Iceland', 'value' => 'Iceland'], + ['name' => 'India', 'value' => 'India'], + ['name' => 'Indonesia', 'value' => 'Indonesia'], + ['name' => 'Iran (Islamic Republic of)', 'value' => 'Iran (Islamic Republic of)'], + ['name' => 'Iraq', 'value' => 'Iraq'], + ['name' => 'Ireland', 'value' => 'Ireland'], + ['name' => 'Israel', 'value' => 'Israel'], + ['name' => 'Italy', 'value' => 'Italy'], + ['name' => 'Jamaica', 'value' => 'Jamaica'], + ['name' => 'Japan', 'value' => 'Japan'], + ['name' => 'Jordan', 'value' => 'Jordan'], + ['name' => 'Kazakhstan', 'value' => 'Kazakhstan'], + ['name' => 'Kenya', 'value' => 'Kenya'], + ['name' => 'Kiribati', 'value' => 'Kiribati'], + ['name' => 'Korea, Republic of', 'value' => 'Korea, Republic of'], + ['name' => 'Kuwait', 'value' => 'Kuwait'], + ['name' => 'Kyrgyzstan', 'value' => 'Kyrgyzstan'], + ['name' => 'Laos', 'value' => 'Laos'], + ['name' => 'Latvia', 'value' => 'Latvia'], + ['name' => 'Lebanon', 'value' => 'Lebanon'], + ['name' => 'Lesotho', 'value' => 'Lesotho'], + ['name' => 'Liberia', 'value' => 'Liberia'], + ['name' => 'Libyan Arab Jamahiriya', 'value' => 'Libyan Arab Jamahiriya'], + ['name' => 'Liechtenstein', 'value' => 'Liechtenstein'], + ['name' => 'Lithuania', 'value' => 'Lithuania'], + ['name' => 'Luxembourg', 'value' => 'Luxembourg'], + ['name' => 'Macao', 'value' => 'Macao'], + ['name' => 'Macedonia, The Former Yugoslav Republic of', 'value' => 'Macedonia, The Former Yugoslav Republic of'], + ['name' => 'Madagascar', 'value' => 'Madagascar'], + ['name' => 'Malawi', 'value' => 'Malawi'], + ['name' => 'Malaysia', 'value' => 'Malaysia'], + ['name' => 'Maldives', 'value' => 'Maldives'], + ['name' => 'Mali', 'value' => 'Mali'], + ['name' => 'Malta', 'value' => 'Malta'], + ['name' => 'Marshall Islands', 'value' => 'Marshall Islands'], + ['name' => 'Martinique', 'value' => 'Martinique'], + ['name' => 'Mauritania', 'value' => 'Mauritania'], + ['name' => 'Mauritius', 'value' => 'Mauritius'], + ['name' => 'Mayotte', 'value' => 'Mayotte'], + ['name' => 'Mexico', 'value' => 'Mexico'], + ['name' => 'Micronesia, Federated States of', 'value' => 'Micronesia, Federated States of'], + ['name' => 'Moldova, Republic of', 'value' => 'Moldova, Republic of'], + ['name' => 'Monaco', 'value' => 'Monaco'], + ['name' => 'Mongolia', 'value' => 'Mongolia'], + ['name' => 'Montserrat', 'value' => 'Montserrat'], + ['name' => 'Morocco', 'value' => 'Morocco'], + ['name' => 'Mozambique', 'value' => 'Mozambique'], + ['name' => 'Myanmar', 'value' => 'Myanmar'], + ['name' => 'Namibia', 'value' => 'Namibia'], + ['name' => 'Nauru', 'value' => 'Nauru'], + ['name' => 'Nepal', 'value' => 'Nepal'], + ['name' => 'Netherlands', 'value' => 'Netherlands'], + ['name' => 'Netherlands Antilles', 'value' => 'Netherlands Antilles'], + ['name' => 'New Caledonia', 'value' => 'New Caledonia'], + ['name' => 'New Zealand', 'value' => 'New Zealand'], + ['name' => 'Nicaragua', 'value' => 'Nicaragua'], + ['name' => 'Niger', 'value' => 'Niger'], + ['name' => 'Nigeria', 'value' => 'Nigeria'], + ['name' => 'Niue', 'value' => 'Niue'], + ['name' => 'Norfolk Island', 'value' => 'Norfolk Island'], + ['name' => 'Northern Mariana Islands', 'value' => 'Northern Mariana Islands'], + ['name' => 'Norway', 'value' => 'Norway'], + ['name' => 'Oman', 'value' => 'Oman'], + ['name' => 'Pakistan', 'value' => 'Pakistan'], + ['name' => 'Palau', 'value' => 'Palau'], + ['name' => 'Palestinian Territory, Occupied', 'value' => 'Palestinian Territory, Occupied'], + ['name' => 'Panama', 'value' => 'Panama'], + ['name' => 'Papua New Guinea', 'value' => 'Papua New Guinea'], + ['name' => 'Paraguay', 'value' => 'Paraguay'], + ['name' => 'Peru', 'value' => 'Peru'], + ['name' => 'Philippines', 'value' => 'Philippines'], + ['name' => 'Pitcairn', 'value' => 'Pitcairn'], + ['name' => 'Poland', 'value' => 'Poland'], + ['name' => 'Portugal', 'value' => 'Portugal'], + ['name' => 'Puerto Rico', 'value' => 'Puerto Rico'], + ['name' => 'Qatar', 'value' => 'Qatar'], + ['name' => 'Reunion', 'value' => 'Reunion'], + ['name' => 'Romania', 'value' => 'Romania'], + ['name' => 'Russian Federation', 'value' => 'Russian Federation'], + ['name' => 'Rwanda', 'value' => 'Rwanda'], + ['name' => 'Saint Kitts and Nevis', 'value' => 'Saint Kitts and Nevis'], + ['name' => 'Saint Lucia', 'value' => 'Saint Lucia'], + ['name' => 'Saint Vincent and the Grenadines', 'value' => 'Saint Vincent and the Grenadines'], + ['name' => 'Samoa', 'value' => 'Samoa'], + ['name' => 'San Marino', 'value' => 'San Marino'], + ['name' => 'Sao Tome and Principe', 'value' => 'Sao Tome and Principe'], + ['name' => 'Saudi Arabia', 'value' => 'Saudi Arabia'], + ['name' => 'Senegal', 'value' => 'Senegal'], + ['name' => 'Seychelles', 'value' => 'Seychelles'], + ['name' => 'Sierra Leone', 'value' => 'Sierra Leone'], + ['name' => 'Singapore', 'value' => 'Singapore'], + ['name' => 'Slovakia', 'value' => 'Slovakia'], + ['name' => 'Slovenia', 'value' => 'Slovenia'], + ['name' => 'Solomon Islands', 'value' => 'Solomon Islands'], + ['name' => 'Somalia', 'value' => 'Somalia'], + ['name' => 'South Africa', 'value' => 'South Africa'], + ['name' => 'Spain', 'value' => 'Spain'], + ['name' => 'Sri Lanka', 'value' => 'Sri Lanka'], + ['name' => 'Sudan', 'value' => 'Sudan'], + ['name' => 'Suriname', 'value' => 'Suriname'], + ['name' => 'Svalbard and Jan Mayen', 'value' => 'Svalbard and Jan Mayen'], + ['name' => 'Swaziland', 'value' => 'Swaziland'], + ['name' => 'Sweden', 'value' => 'Sweden'], + ['name' => 'Switzerland', 'value' => 'Switzerland'], + ['name' => 'Syrian Arab Republic', 'value' => 'Syrian Arab Republic'], + ['name' => 'Tajikistan', 'value' => 'Tajikistan'], + ['name' => 'Thailand', 'value' => 'Thailand'], + ['name' => 'Togo', 'value' => 'Togo'], + ['name' => 'Tokelau', 'value' => 'Tokelau'], + ['name' => 'Tonga', 'value' => 'Tonga'], + ['name' => 'Trinidad and Tobago', 'value' => 'Trinidad and Tobago'], + ['name' => 'Tunisia', 'value' => 'Tunisia'], + ['name' => 'Turkey', 'value' => 'Turkey'], + ['name' => 'Turkmenistan', 'value' => 'Turkmenistan'], + ['name' => 'Turks and Caicos Islands', 'value' => 'Turks and Caicos Islands'], + ['name' => 'Tuvalu', 'value' => 'Tuvalu'], + ['name' => 'Uganda', 'value' => 'Uganda'], + ['name' => 'Ukraine', 'value' => 'Ukraine'], + ['name' => 'United Arab Emirates', 'value' => 'United Arab Emirates'], + ['name' => 'United Kingdom', 'value' => 'United Kingdom'], + ['name' => 'United States', 'value' => 'United States'], + ['name' => 'United States Minor Outlying Islands', 'value' => 'United States Minor Outlying Islands'], + ['name' => 'Uruguay', 'value' => 'Uruguay'], + ['name' => 'Uzbekistan', 'value' => 'Uzbekistan'], + ['name' => 'Vanuatu', 'value' => 'Vanuatu'], + ['name' => 'Venezuela', 'value' => 'Venezuela'], + ['name' => 'Viet Nam', 'value' => 'Viet Nam'], + ['name' => 'Virgin Islands (British)', 'value' => 'Virgin Islands (British)'], + ['name' => 'Virgin Islands (U.S.)', 'value' => 'Virgin Islands (U.S.)'], + ['name' => 'Wallis and Futuna Islands', 'value' => 'Wallis and Futuna Islands'], + ['name' => 'Western Sahara', 'value' => 'Western Sahara'], + ['name' => 'Yemen', 'value' => 'Yemen'], + ['name' => 'Zambia', 'value' => 'Zambia'], + ['name' => 'Zimbabwe', 'value' => 'Zimbabwe'], + ]; + $countries = $this->table('countries'); + $countries->insert($data)->save(); + } +} diff --git a/models/Address.php b/models/Address.php new file mode 100644 index 0000000000000000000000000000000000000000..1bfa4503e34d2bb252c34375167b65961120f8a0 --- /dev/null +++ b/models/Address.php @@ -0,0 +1,113 @@ +<?php + +namespace models; + +use db\Database; +use PDO; +use core\App; + + +class Address +{ + private $db; + + public function __construct() + { + $this->db = App::resolve(Database::class); + } + + public function getAddresses($userid) + { + $query = "SELECT * FROM address WHERE userid = :userid"; + $params = [':userid' => $userid]; + return $this->db->query($query, $params)->fetchall(PDO::FETCH_ASSOC); + } + + public function addAddress($street, $city, $country, $zipcode, $district, $id) + { + $query = "INSERT INTO address (street, city, country, zipcode, district, userid) VALUES (:street, :city, :country, :zipcode, :district, :userid)"; + $params = [ + ':street' => $street, + ':city' => $city, + ':country' => $country, + ':zipcode' => $zipcode, + ':district' => $district, + ':userid' => $id + ]; + + + if ($this->db->query($query, $params)) { + return $this->db->connection->lastInsertId(); + } + } + + public function deleteAddress($id): bool + { + $query = "DELETE FROM address WHERE id = :id"; + $params = [':id' => $id]; + if ($this->db->query($query, $params)) { + return true; + } + } + + public function getAddress($id): array + { + $query = "SELECT * FROM address WHERE id = :id"; + $params = [':id' => $id]; + return $this->db->query($query, $params)->fetch(PDO::FETCH_ASSOC); + } + + public function updateAddress($id, $street, $city, $country, $zipcode, $district, $user_id): bool + { + $query = "UPDATE address SET street = :street, city = :city, country = :country, zipcode = :zipcode, district = :district, userid = :userid WHERE id = :id"; + $params = [ + ':street' => $street, + ':city' => $city, + ':country' => $country, + ':zipcode' => $zipcode, + ':district' => $district, + ':id' => $id, + ':userid' => $user_id + ]; + if ($this->db->query($query, $params)) { + return true; + } + } + + public function setMainAddress($userID, $addressID): bool + { + $query = "UPDATE address SET main = 0 WHERE id!= :addressID AND userid = :userID"; + $params = [ + ':addressID' => $addressID, + ':userID' => $userID + ]; + + if ($this->db->query($query, $params)) { + $query = "UPDATE address SET main = 1 WHERE id = :addressID"; + $params = [ + ':addressID' => $addressID + ]; + $this->db->query($query, $params); + return true; + } + } + + public function getMainAddress($id) + { + $query = "SELECT * FROM address WHERE main = '1' AND userid = :id"; + $params = [ + ':id' => $id + ]; + + return $this->db->query($query, $params)->fetch(); + } + + public function addAddressSetMain($street, $city, $country, $zipcode, $district, $user_id, $main) + { + $this->addAddress($street, $city, $country, $zipcode, $district, $user_id); + if ($main) { + $this->setMainAddress($user_id, $this->db->connection->lastInsertId()); + } + } + +} \ No newline at end of file diff --git a/models/Country.php b/models/Country.php new file mode 100644 index 0000000000000000000000000000000000000000..f9987851f84a4f0d6003ebb0f538536a4e8ccd7c --- /dev/null +++ b/models/Country.php @@ -0,0 +1,29 @@ +<?php + +namespace models; +use db\Database; +use core\App; +use PDO; + +class Country +{ + + private $db; + + public function __construct() + { + $this->db = App::resolve(Database::class); + } + + public function getCountries() + { + $sql = "SELECT * FROM countries"; + $stmt = $this->db->query($sql); + return $stmt->fetchAll(PDO::FETCH_CLASS); + } + + + + + +} \ No newline at end of file diff --git a/models/User.php b/models/User.php index 979f7ecd7679d50c6f4d678ee5b2b91ece10d26a..7486eaec82c99ab0f1eac877b36d1a65aef1700e 100644 --- a/models/User.php +++ b/models/User.php @@ -11,6 +11,7 @@ class User private $db; + public function __construct() { $this->db = App::resolve(Database::class); @@ -25,8 +26,12 @@ class User public function delete(int $id): bool { + $query = "DELETE FROM address WHERE userid = :id"; + $params = [':id' => $id]; + $this->db->query($query, $params); $query = "DELETE FROM users WHERE id = :id"; $params = [':id' => $id]; + if ($this->db->query($query, $params)) { return true; } @@ -39,38 +44,63 @@ class User session_destroy(); } - public function register(string $name, string $email, string $password, string $address, int|null $is_admin): bool - { + public function register( + string $name, + string $email, + string $password, + string $street, + string $zipcode, + string $country, + string $city, + string $district, + ?int $is_admin + ): bool { $hashedPassword = password_hash($password, PASSWORD_BCRYPT); - $query = "INSERT INTO users (name, email, password, address, is_admin) VALUES (:name, :email, :password, :address, :is_admin)"; + $query = "INSERT INTO users (name, email, password, is_admin) VALUES (:name, :email, :password, :is_admin)"; $params = [ ':name' => $name, ':email' => $email, ':password' => $hashedPassword, - ':address' => $address, ':is_admin' => $is_admin ]; + $this->db->query($query, $params); + $userid = $this->db->connection->lastInsertId(); + + $query = "INSERT INTO address (street, zipcode, city, district, country, userid, main) + VALUES (:street, :zipcode, :city, :district, :country, :userid, '1')"; + $params = [ + ':userid' => $userid, + ':street' => $street, + ':zipcode' => $zipcode, + ':city' => $city, + ':district' => $district, + ':country' => $country + ]; + + if ($this->db->query($query, $params)) { return true; } } - public function getByEmail(string $email) + public function checkByEmail($email) { $query = "SELECT * FROM users WHERE email = :email"; $params = [':email' => $email]; - return $this->db->query($query, $params)->fetch(PDO::FETCH_ASSOC); + if ($this->db->query($query, $params)->fetch(PDO::FETCH_ASSOC)) { + return true; + } } - public function update(int $id, string $name, string $email, string $address, int|null $is_admin): bool + + public function update(int $id, string $name, string $email, int|null $is_admin): bool { - $query = "UPDATE users SET name = :name, email = :email, address = :address, is_admin = :is_admin WHERE id = :id"; + $query = "UPDATE users SET name = :name, email = :email, is_admin = :is_admin WHERE id = :id"; $params = [ ':name' => $name, ':email' => $email, - ':address' => $address, ':is_admin' => $is_admin, // Include is_admin in the parameters ':id' => $id ]; @@ -81,7 +111,23 @@ class User public function getAll(): array { - $query = "SELECT * FROM users"; + $query = " + SELECT + users.*, + address.street, + address.zipcode, + address.city, + address.district, + address.country, + address.main + FROM + users + LEFT JOIN + address + ON + users.id = address.userid + "; + return ($this->db->query($query)->fetchAll(PDO::FETCH_ASSOC)); } @@ -98,7 +144,7 @@ class User ':password' => $hashedPassword, 'u_id' => $u_id, ]; - if ($this->db->query($queryPassword, $params)){ + if ($this->db->query($queryPassword, $params)) { return true; } } @@ -119,19 +165,27 @@ class User ':expires_at' => $expiry ]; - if ($this->db->query($query, $params)){ + if ($this->db->query($query, $params)) { return true; } } } - public function login($password, $user): bool + public function login($email, $password): bool { + $query = "SELECT * FROM users WHERE email = :email"; + $params = [ + ':email' => $email, + ]; + + $user = $this->db->query($query, $params)->fetch(PDO::FETCH_ASSOC); + if ($user && password_verify($password, $user['password'])) { - $_SESSION['user'] = $user; // Store user info in session + $_SESSION['user'] = $user; return true; } else { return false; } } + } \ No newline at end of file diff --git a/phinx.php b/phinx.php new file mode 100644 index 0000000000000000000000000000000000000000..843103163fc5277d7c02eefc5aeb71e16ba8cfc5 --- /dev/null +++ b/phinx.php @@ -0,0 +1,41 @@ +<?php + +return +[ + 'paths' => [ + 'migrations' => '%%PHINX_CONFIG_DIR%%/db/migrations', + 'seeds' => '%%PHINX_CONFIG_DIR%%/db/seeds' + ], + 'environments' => [ + 'default_migration_table' => 'phinxlog', + 'default_environment' => 'development', + 'production' => [ + 'adapter' => 'mysql', + 'host' => 'localhost', + 'name' => 'production_db', + 'user' => 'root', + 'pass' => '', + 'port' => '3306', + 'charset' => 'utf8', + ], + 'development' => [ + 'adapter' => 'mysql', + 'host' => 'localhost', + 'name' => 'ecommerce_db', + 'user' => 'root', + 'pass' => '', + 'port' => '3306', + 'charset' => 'utf8', + ], + 'testing' => [ + 'adapter' => 'mysql', + 'host' => 'localhost', + 'name' => 'testing_db', + 'user' => 'root', + 'pass' => '', + 'port' => '3306', + 'charset' => 'utf8', + ] + ], + 'version_order' => 'creation' +]; diff --git a/public/css/styles.css b/public/css/styles.css index 122ab3599e299e1c0b4808b9f6433d535945ff2b..86b58fe5dff0c2fd23159388cb78713cc708330f 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -5,6 +5,7 @@ table { width: 100%; border-collapse: collapse; + margin-bottom: 20px; } th, td { @@ -52,6 +53,10 @@ header h1 { flex-grow: 1; /* Allows the h1 to take up available space */ } +h4{ + margin-bottom: -2px; +} + .auth-links { display: flex; /* Makes the links align horizontally */ margin-left: -180px; /* Adjust this value to move buttons slightly left */ @@ -255,4 +260,8 @@ table tr:hover { .user-info-table th, .user-info-table td { text-align: center; /* Center the text in both header and body cells */ +} + +.address-line { + margin-bottom: 5px; } \ No newline at end of file diff --git a/public/index.php b/public/index.php index 087bbbd7a6ad6d50bac71086466ebf5812fd6ff3..03ee3d9ef172cc5865398cc0e15b0265b1969933 100644 --- a/public/index.php +++ b/public/index.php @@ -1,5 +1,7 @@ <?php + + error_reporting(2); session_start(); date_default_timezone_set('Europe/Lisbon'); @@ -14,8 +16,7 @@ require BASE_PATH . '/vendor/autoload.php'; require BASE_PATH . 'core/functions.php'; require BASE_PATH . '/vendor/autoload.php'; require base_path('bootstrap.php'); - - +require BASE_PATH . '/vendor/autoload.php'; $router = new Router(); diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 0fb0a2c194b8590999a5ed79e357d4a9c1e9d8b8..9bb846c55c5e28463e9e93ad0230b7e037c6f0ac 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -7,4 +7,5 @@ $baseDir = dirname($vendorDir); return array( 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', + 'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php', ); diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index 5a1b67fce9e242e6f1fd7efac021b729a8d62f78..dc0290b1e050f88d53fda3cc5b51a3deff4e641f 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -10,5 +10,25 @@ return array( 'db\\' => array($baseDir . '/db'), 'core\\' => array($baseDir . '/core'), 'controllers\\' => array($baseDir . '/controllers'), + 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), + 'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'), + 'Symfony\\Polyfill\\Intl\\Grapheme\\' => array($vendorDir . '/symfony/polyfill-intl-grapheme'), + 'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'), + 'Symfony\\Contracts\\Service\\' => array($vendorDir . '/symfony/service-contracts'), + 'Symfony\\Component\\String\\' => array($vendorDir . '/symfony/string'), + 'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'), + 'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'), + 'Symfony\\Component\\Config\\' => array($vendorDir . '/symfony/config'), + 'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'), + 'Psr\\Log\\' => array($vendorDir . '/psr/log/src'), + 'Psr\\Container\\' => array($vendorDir . '/psr/container/src'), + 'Psr\\Clock\\' => array($vendorDir . '/psr/clock/src'), + 'Phinx\\' => array($vendorDir . '/robmorgan/phinx/src/Phinx'), 'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'), + 'League\\Container\\' => array($vendorDir . '/league/container/src'), + 'Cake\\Utility\\' => array($vendorDir . '/cakephp/utility'), + 'Cake\\Datasource\\' => array($vendorDir . '/cakephp/datasource'), + 'Cake\\Database\\' => array($vendorDir . '/cakephp/database'), + 'Cake\\Core\\' => array($vendorDir . '/cakephp/core'), + 'Cake\\Chronos\\' => array($vendorDir . '/cakephp/chronos/src'), ); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index 98bdd9e48f2cb1194c902c044b232b2ceb568d14..06c7625f3be2165677f8d9c01db73231af82f1bf 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -33,6 +33,18 @@ class ComposerAutoloaderInit78fc7c15f25f36eabb0edb2494145fe4 $loader->register(true); + $filesToLoad = \Composer\Autoload\ComposerStaticInit78fc7c15f25f36eabb0edb2494145fe4::$files; + $requireFile = \Closure::bind(static function ($fileIdentifier, $file) { + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + + require $file; + } + }, null, null); + foreach ($filesToLoad as $fileIdentifier => $file) { + $requireFile($fileIdentifier, $file); + } + return $loader; } } diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 20ff970db9468bae98e787b08afbbc3e2f049459..ee0bbf8ad37fff9c4c5a83bb45f1b227870d8a0f 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -6,6 +6,17 @@ namespace Composer\Autoload; class ComposerStaticInit78fc7c15f25f36eabb0edb2494145fe4 { + public static $files = array ( + '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', + '72142d7b40a3a0b14e91825290b5ad82' => __DIR__ . '/..' . '/cakephp/core/functions.php', + '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', + '8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php', + 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', + 'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php', + '948ad5488880985ff1c06721a4e447fe' => __DIR__ . '/..' . '/cakephp/utility/bootstrap.php', + ); + public static $prefixLengthsPsr4 = array ( 'm' => array ( @@ -20,10 +31,39 @@ class ComposerStaticInit78fc7c15f25f36eabb0edb2494145fe4 'core\\' => 5, 'controllers\\' => 12, ), + 'S' => + array ( + 'Symfony\\Polyfill\\Mbstring\\' => 26, + 'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33, + 'Symfony\\Polyfill\\Intl\\Grapheme\\' => 31, + 'Symfony\\Polyfill\\Ctype\\' => 23, + 'Symfony\\Contracts\\Service\\' => 26, + 'Symfony\\Component\\String\\' => 25, + 'Symfony\\Component\\Filesystem\\' => 29, + 'Symfony\\Component\\Console\\' => 26, + 'Symfony\\Component\\Config\\' => 25, + ), 'P' => array ( + 'Psr\\SimpleCache\\' => 16, + 'Psr\\Log\\' => 8, + 'Psr\\Container\\' => 14, + 'Psr\\Clock\\' => 10, + 'Phinx\\' => 6, 'PHPMailer\\PHPMailer\\' => 20, ), + 'L' => + array ( + 'League\\Container\\' => 17, + ), + 'C' => + array ( + 'Cake\\Utility\\' => 13, + 'Cake\\Datasource\\' => 16, + 'Cake\\Database\\' => 14, + 'Cake\\Core\\' => 10, + 'Cake\\Chronos\\' => 13, + ), ); public static $prefixDirsPsr4 = array ( @@ -43,14 +83,95 @@ class ComposerStaticInit78fc7c15f25f36eabb0edb2494145fe4 array ( 0 => __DIR__ . '/../..' . '/controllers', ), + 'Symfony\\Polyfill\\Mbstring\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', + ), + 'Symfony\\Polyfill\\Intl\\Normalizer\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer', + ), + 'Symfony\\Polyfill\\Intl\\Grapheme\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme', + ), + 'Symfony\\Polyfill\\Ctype\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-ctype', + ), + 'Symfony\\Contracts\\Service\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/service-contracts', + ), + 'Symfony\\Component\\String\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/string', + ), + 'Symfony\\Component\\Filesystem\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/filesystem', + ), + 'Symfony\\Component\\Console\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/console', + ), + 'Symfony\\Component\\Config\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/config', + ), + 'Psr\\SimpleCache\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/simple-cache/src', + ), + 'Psr\\Log\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/log/src', + ), + 'Psr\\Container\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/container/src', + ), + 'Psr\\Clock\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/clock/src', + ), + 'Phinx\\' => + array ( + 0 => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx', + ), 'PHPMailer\\PHPMailer\\' => array ( 0 => __DIR__ . '/..' . '/phpmailer/phpmailer/src', ), + 'League\\Container\\' => + array ( + 0 => __DIR__ . '/..' . '/league/container/src', + ), + 'Cake\\Utility\\' => + array ( + 0 => __DIR__ . '/..' . '/cakephp/utility', + ), + 'Cake\\Datasource\\' => + array ( + 0 => __DIR__ . '/..' . '/cakephp/datasource', + ), + 'Cake\\Database\\' => + array ( + 0 => __DIR__ . '/..' . '/cakephp/database', + ), + 'Cake\\Core\\' => + array ( + 0 => __DIR__ . '/..' . '/cakephp/core', + ), + 'Cake\\Chronos\\' => + array ( + 0 => __DIR__ . '/..' . '/cakephp/chronos/src', + ), ); public static $classMap = array ( 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + 'Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php', ); public static function getInitializer(ClassLoader $loader) diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 2a250a13c5d4072bd0232e6aeff3cd25d2cca7c1..bec6f91df61a0492cf1d5a0a859fc96462750e40 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -1,5 +1,409 @@ { "packages": [ + { + "name": "cakephp/chronos", + "version": "3.1.0", + "version_normalized": "3.1.0.0", + "source": { + "type": "git", + "url": "https://github.com/cakephp/chronos.git", + "reference": "786d69e1ee4b735765cbdb5521b9603e9b98d650" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/chronos/zipball/786d69e1ee4b735765cbdb5521b9603e9b98d650", + "reference": "786d69e1ee4b735765cbdb5521b9603e9b98d650", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/clock": "^1.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "cakephp/cakephp-codesniffer": "^5.0", + "phpunit/phpunit": "^10.1.0 || ^11.1.3" + }, + "time": "2024-07-18T03:18:04+00:00", + "type": "library", + "installation-source": "source", + "autoload": { + "psr-4": { + "Cake\\Chronos\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "http://nesbot.com" + }, + { + "name": "The CakePHP Team", + "homepage": "https://cakephp.org" + } + ], + "description": "A simple API extension for DateTime.", + "homepage": "https://cakephp.org", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "issues": "https://github.com/cakephp/chronos/issues", + "source": "https://github.com/cakephp/chronos" + }, + "install-path": "../cakephp/chronos" + }, + { + "name": "cakephp/core", + "version": "5.1.2", + "version_normalized": "5.1.2.0", + "source": { + "type": "git", + "url": "https://github.com/cakephp/core.git", + "reference": "70f99f5df4dde8677ddcf6245654a8b937f7786b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/core/zipball/70f99f5df4dde8677ddcf6245654a8b937f7786b", + "reference": "70f99f5df4dde8677ddcf6245654a8b937f7786b", + "shasum": "" + }, + "require": { + "cakephp/utility": "^5.1", + "league/container": "^4.2", + "php": ">=8.1", + "psr/container": "^1.1 || ^2.0" + }, + "provide": { + "psr/container-implementation": "^2.0" + }, + "suggest": { + "cakephp/cache": "To use Configure::store() and restore().", + "cakephp/event": "To use PluginApplicationInterface or plugin applications.", + "league/container": "To use Container and ServiceProvider classes" + }, + "time": "2024-10-31T06:55:29+00:00", + "type": "library", + "installation-source": "source", + "autoload": { + "files": [ + "functions.php" + ], + "psr-4": { + "Cake\\Core\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/core/graphs/contributors" + } + ], + "description": "CakePHP Framework Core classes", + "homepage": "https://cakephp.org", + "keywords": [ + "cakephp", + "core", + "framework" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/cakephp/issues", + "source": "https://github.com/cakephp/core" + }, + "install-path": "../cakephp/core" + }, + { + "name": "cakephp/database", + "version": "5.1.2", + "version_normalized": "5.1.2.0", + "source": { + "type": "git", + "url": "https://github.com/cakephp/database.git", + "reference": "0266cba5968f089cbca8785dbae4450623a6f055" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/database/zipball/0266cba5968f089cbca8785dbae4450623a6f055", + "reference": "0266cba5968f089cbca8785dbae4450623a6f055", + "shasum": "" + }, + "require": { + "cakephp/chronos": "^3.1", + "cakephp/core": "^5.1", + "cakephp/datasource": "^5.1", + "php": ">=8.1", + "psr/log": "^3.0" + }, + "require-dev": { + "cakephp/i18n": "^5.1", + "cakephp/log": "^5.1" + }, + "suggest": { + "cakephp/i18n": "If you are using locale-aware datetime formats.", + "cakephp/log": "If you want to use query logging without providing a logger yourself." + }, + "time": "2024-11-02T13:34:55+00:00", + "type": "library", + "installation-source": "source", + "autoload": { + "psr-4": { + "Cake\\Database\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/database/graphs/contributors" + } + ], + "description": "Flexible and powerful Database abstraction library with a familiar PDO-like API", + "homepage": "https://cakephp.org", + "keywords": [ + "abstraction", + "cakephp", + "database", + "database abstraction", + "pdo" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/cakephp/issues", + "source": "https://github.com/cakephp/database" + }, + "install-path": "../cakephp/database" + }, + { + "name": "cakephp/datasource", + "version": "5.1.2", + "version_normalized": "5.1.2.0", + "source": { + "type": "git", + "url": "https://github.com/cakephp/datasource.git", + "reference": "9ffde854a988cadc740f419b602f9872260908a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/datasource/zipball/9ffde854a988cadc740f419b602f9872260908a8", + "reference": "9ffde854a988cadc740f419b602f9872260908a8", + "shasum": "" + }, + "require": { + "cakephp/core": "^5.1", + "php": ">=8.1", + "psr/simple-cache": "^2.0 || ^3.0" + }, + "require-dev": { + "cakephp/cache": "^5.1", + "cakephp/collection": "^5.1", + "cakephp/utility": "^5.1" + }, + "suggest": { + "cakephp/cache": "If you decide to use Query caching.", + "cakephp/collection": "If you decide to use ResultSetInterface.", + "cakephp/utility": "If you decide to use EntityTrait." + }, + "time": "2024-10-17T08:33:47+00:00", + "type": "library", + "installation-source": "source", + "autoload": { + "psr-4": { + "Cake\\Datasource\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/datasource/graphs/contributors" + } + ], + "description": "Provides connection managing and traits for Entities and Queries that can be reused for different datastores", + "homepage": "https://cakephp.org", + "keywords": [ + "cakephp", + "connection management", + "datasource", + "entity", + "query" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/cakephp/issues", + "source": "https://github.com/cakephp/datasource" + }, + "install-path": "../cakephp/datasource" + }, + { + "name": "cakephp/utility", + "version": "5.1.2", + "version_normalized": "5.1.2.0", + "source": { + "type": "git", + "url": "https://github.com/cakephp/utility.git", + "reference": "a1a503e9ef96761242eb260df0e04eb0e068e5cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/utility/zipball/a1a503e9ef96761242eb260df0e04eb0e068e5cd", + "reference": "a1a503e9ef96761242eb260df0e04eb0e068e5cd", + "shasum": "" + }, + "require": { + "cakephp/core": "^5.1", + "php": ">=8.1" + }, + "suggest": { + "ext-intl": "To use Text::transliterate() or Text::slug()", + "lib-ICU": "To use Text::transliterate() or Text::slug()" + }, + "time": "2024-11-08T14:07:41+00:00", + "type": "library", + "installation-source": "source", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Cake\\Utility\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/utility/graphs/contributors" + } + ], + "description": "CakePHP Utility classes such as Inflector, String, Hash, and Security", + "homepage": "https://cakephp.org", + "keywords": [ + "cakephp", + "hash", + "inflector", + "security", + "string", + "utility" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/cakephp/issues", + "source": "https://github.com/cakephp/utility" + }, + "install-path": "../cakephp/utility" + }, + { + "name": "league/container", + "version": "4.2.4", + "version_normalized": "4.2.4.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/container.git", + "reference": "7ea728b013b9a156c409c6f0fc3624071b742dec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/container/zipball/7ea728b013b9a156c409c6f0fc3624071b742dec", + "reference": "7ea728b013b9a156c409c6f0fc3624071b742dec", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "psr/container": "^1.1 || ^2.0" + }, + "provide": { + "psr/container-implementation": "^1.0" + }, + "replace": { + "orno/di": "~2.0" + }, + "require-dev": { + "nette/php-generator": "^3.4", + "nikic/php-parser": "^4.10", + "phpstan/phpstan": "^0.12.47", + "phpunit/phpunit": "^8.5.17", + "roave/security-advisories": "dev-latest", + "scrutinizer/ocular": "^1.8", + "squizlabs/php_codesniffer": "^3.6" + }, + "time": "2024-11-10T12:42:13+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev", + "dev-4.x": "4.x-dev", + "dev-3.x": "3.x-dev", + "dev-2.x": "2.x-dev", + "dev-1.x": "1.x-dev" + } + }, + "installation-source": "source", + "autoload": { + "psr-4": { + "League\\Container\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Phil Bennett", + "email": "mail@philbennett.co.uk", + "role": "Developer" + } + ], + "description": "A fast and intuitive dependency injection container.", + "homepage": "https://github.com/thephpleague/container", + "keywords": [ + "container", + "dependency", + "di", + "injection", + "league", + "provider", + "service" + ], + "support": { + "issues": "https://github.com/thephpleague/container/issues", + "source": "https://github.com/thephpleague/container/tree/4.2.4" + }, + "funding": [ + { + "url": "https://github.com/philipobenito", + "type": "github" + } + ], + "install-path": "../league/container" + }, { "name": "phpmailer/phpmailer", "version": "v6.9.2", @@ -83,6 +487,1129 @@ } ], "install-path": "../phpmailer/phpmailer" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "time": "2022-11-25T14:36:26+00:00", + "type": "library", + "installation-source": "source", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "install-path": "../psr/clock" + }, + { + "name": "psr/container", + "version": "2.0.2", + "version_normalized": "2.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "time": "2021-11-05T16:47:00+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "installation-source": "source", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "install-path": "../psr/container" + }, + { + "name": "psr/log", + "version": "3.0.2", + "version_normalized": "3.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "time": "2024-09-11T13:17:53+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "installation-source": "source", + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "install-path": "../psr/log" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "version_normalized": "3.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "time": "2021-10-29T13:26:27+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "installation-source": "source", + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "install-path": "../psr/simple-cache" + }, + { + "name": "robmorgan/phinx", + "version": "0.16.5", + "version_normalized": "0.16.5.0", + "source": { + "type": "git", + "url": "https://github.com/cakephp/phinx.git", + "reference": "31d837c17d6dc294d44f0b17331066548ac8c032" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/phinx/zipball/31d837c17d6dc294d44f0b17331066548ac8c032", + "reference": "31d837c17d6dc294d44f0b17331066548ac8c032", + "shasum": "" + }, + "require": { + "cakephp/database": "^5.0.2", + "composer-runtime-api": "^2.0", + "php-64bit": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/config": "^3.4|^4.0|^5.0|^6.0|^7.0", + "symfony/console": "^6.0|^7.0" + }, + "require-dev": { + "cakephp/cakephp": "^5.0.2", + "cakephp/cakephp-codesniffer": "^5.0", + "ext-json": "*", + "ext-pdo": "*", + "phpunit/phpunit": "^9.5.19", + "symfony/yaml": "^3.4|^4.0|^5.0|^6.0|^7.0" + }, + "suggest": { + "ext-json": "Install if using JSON configuration format", + "ext-pdo": "PDO extension is needed", + "symfony/yaml": "Install if using YAML configuration format" + }, + "time": "2024-10-03T15:41:04+00:00", + "bin": [ + "bin/phinx" + ], + "type": "library", + "installation-source": "source", + "autoload": { + "psr-4": { + "Phinx\\": "src/Phinx/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Rob Morgan", + "email": "robbym@gmail.com", + "homepage": "https://robmorgan.id.au", + "role": "Lead Developer" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com", + "homepage": "https://shadowhand.me", + "role": "Developer" + }, + { + "name": "Richard Quadling", + "email": "rquadling@gmail.com", + "role": "Developer" + }, + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/phinx/graphs/contributors", + "role": "Developer" + } + ], + "description": "Phinx makes it ridiculously easy to manage the database migrations for your PHP app.", + "homepage": "https://phinx.org", + "keywords": [ + "database", + "database migrations", + "db", + "migrations", + "phinx" + ], + "support": { + "issues": "https://github.com/cakephp/phinx/issues", + "source": "https://github.com/cakephp/phinx/tree/0.16.5" + }, + "install-path": "../robmorgan/phinx" + }, + { + "name": "symfony/config", + "version": "v7.2.0", + "version_normalized": "7.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "bcd3c4adf0144dee5011bb35454728c38adec055" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/bcd3c4adf0144dee5011bb35454728c38adec055", + "reference": "bcd3c4adf0144dee5011bb35454728c38adec055", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/filesystem": "^7.1", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/finder": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "require-dev": { + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0" + }, + "time": "2024-11-04T11:36:24+00:00", + "type": "library", + "installation-source": "source", + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/config" + }, + { + "name": "symfony/console", + "version": "v7.2.0", + "version_normalized": "7.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "23c8aae6d764e2bae02d2a99f7532a7f6ed619cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/23c8aae6d764e2bae02d2a99f7532a7f6ed619cf", + "reference": "23c8aae6d764e2bae02d2a99f7532a7f6ed619cf", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^6.4|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "time": "2024-11-06T14:24:19+00:00", + "type": "library", + "installation-source": "source", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/console" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.1", + "version_normalized": "3.5.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "time": "2024-09-25T14:20:29+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "installation-source": "source", + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/deprecation-contracts" + }, + { + "name": "symfony/filesystem", + "version": "v7.2.0", + "version_normalized": "7.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^6.4|^7.0" + }, + "time": "2024-10-25T15:15:23+00:00", + "type": "library", + "installation-source": "source", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/filesystem" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.31.0", + "version_normalized": "1.31.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "time": "2024-09-09T11:45:10+00:00", + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "installation-source": "source", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/polyfill-ctype" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.31.0", + "version_normalized": "1.31.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "time": "2024-09-09T11:45:10+00:00", + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "installation-source": "source", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/polyfill-intl-grapheme" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.31.0", + "version_normalized": "1.31.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "time": "2024-09-09T11:45:10+00:00", + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "installation-source": "source", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/polyfill-intl-normalizer" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.31.0", + "version_normalized": "1.31.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "time": "2024-09-09T11:45:10+00:00", + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "installation-source": "source", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/polyfill-mbstring" + }, + { + "name": "symfony/service-contracts", + "version": "v3.5.1", + "version_normalized": "3.5.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "time": "2024-09-25T14:20:29+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "installation-source": "source", + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/service-contracts" + }, + { + "name": "symfony/string", + "version": "v7.2.0", + "version_normalized": "7.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82", + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "time": "2024-11-13T13:31:26+00:00", + "type": "library", + "installation-source": "source", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/string" } ], "dev": true, diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 443455fb7d1a5f2198dc0c8189a8be121b49880f..f92c36225b26a3c201d5165bda6a9d0312c8440e 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -3,22 +3,82 @@ 'name' => 'josedomingues/simple-ecommerce-system', 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => '7ef61e3bffe9419a7555d559e93bc61cdf7f0d87', + 'reference' => '951df679d86c0b3ec9966e14874f12a4a8d9a3db', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev' => true, ), 'versions' => array( + 'cakephp/chronos' => array( + 'pretty_version' => '3.1.0', + 'version' => '3.1.0.0', + 'reference' => '786d69e1ee4b735765cbdb5521b9603e9b98d650', + 'type' => 'library', + 'install_path' => __DIR__ . '/../cakephp/chronos', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'cakephp/core' => array( + 'pretty_version' => '5.1.2', + 'version' => '5.1.2.0', + 'reference' => '70f99f5df4dde8677ddcf6245654a8b937f7786b', + 'type' => 'library', + 'install_path' => __DIR__ . '/../cakephp/core', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'cakephp/database' => array( + 'pretty_version' => '5.1.2', + 'version' => '5.1.2.0', + 'reference' => '0266cba5968f089cbca8785dbae4450623a6f055', + 'type' => 'library', + 'install_path' => __DIR__ . '/../cakephp/database', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'cakephp/datasource' => array( + 'pretty_version' => '5.1.2', + 'version' => '5.1.2.0', + 'reference' => '9ffde854a988cadc740f419b602f9872260908a8', + 'type' => 'library', + 'install_path' => __DIR__ . '/../cakephp/datasource', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'cakephp/utility' => array( + 'pretty_version' => '5.1.2', + 'version' => '5.1.2.0', + 'reference' => 'a1a503e9ef96761242eb260df0e04eb0e068e5cd', + 'type' => 'library', + 'install_path' => __DIR__ . '/../cakephp/utility', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'josedomingues/simple-ecommerce-system' => array( 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => '7ef61e3bffe9419a7555d559e93bc61cdf7f0d87', + 'reference' => '951df679d86c0b3ec9966e14874f12a4a8d9a3db', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev_requirement' => false, ), + 'league/container' => array( + 'pretty_version' => '4.2.4', + 'version' => '4.2.4.0', + 'reference' => '7ea728b013b9a156c409c6f0fc3624071b742dec', + 'type' => 'library', + 'install_path' => __DIR__ . '/../league/container', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'orno/di' => array( + 'dev_requirement' => false, + 'replaced' => array( + 0 => '~2.0', + ), + ), 'phpmailer/phpmailer' => array( 'pretty_version' => 'v6.9.2', 'version' => '6.9.2.0', @@ -28,5 +88,159 @@ 'aliases' => array(), 'dev_requirement' => false, ), + 'psr/clock' => array( + 'pretty_version' => '1.0.0', + 'version' => '1.0.0.0', + 'reference' => 'e41a24703d4560fd0acb709162f73b8adfc3aa0d', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/clock', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/clock-implementation' => array( + 'dev_requirement' => false, + 'provided' => array( + 0 => '1.0', + ), + ), + 'psr/container' => array( + 'pretty_version' => '2.0.2', + 'version' => '2.0.2.0', + 'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/container', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/container-implementation' => array( + 'dev_requirement' => false, + 'provided' => array( + 0 => '^1.0', + 1 => '^2.0', + ), + ), + 'psr/log' => array( + 'pretty_version' => '3.0.2', + 'version' => '3.0.2.0', + 'reference' => 'f16e1d5863e37f8d8c2a01719f5b34baa2b714d3', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/log', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/log-implementation' => array( + 'dev_requirement' => false, + 'provided' => array( + 0 => '1.0|2.0|3.0', + ), + ), + 'psr/simple-cache' => array( + 'pretty_version' => '3.0.0', + 'version' => '3.0.0.0', + 'reference' => '764e0b3939f5ca87cb904f570ef9be2d78a07865', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/simple-cache', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'robmorgan/phinx' => array( + 'pretty_version' => '0.16.5', + 'version' => '0.16.5.0', + 'reference' => '31d837c17d6dc294d44f0b17331066548ac8c032', + 'type' => 'library', + 'install_path' => __DIR__ . '/../robmorgan/phinx', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'symfony/config' => array( + 'pretty_version' => 'v7.2.0', + 'version' => '7.2.0.0', + 'reference' => 'bcd3c4adf0144dee5011bb35454728c38adec055', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/config', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'symfony/console' => array( + 'pretty_version' => 'v7.2.0', + 'version' => '7.2.0.0', + 'reference' => '23c8aae6d764e2bae02d2a99f7532a7f6ed619cf', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/console', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'symfony/deprecation-contracts' => array( + 'pretty_version' => 'v3.5.1', + 'version' => '3.5.1.0', + 'reference' => '74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/deprecation-contracts', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'symfony/filesystem' => array( + 'pretty_version' => 'v7.2.0', + 'version' => '7.2.0.0', + 'reference' => 'b8dce482de9d7c9fe2891155035a7248ab5c7fdb', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/filesystem', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'symfony/polyfill-ctype' => array( + 'pretty_version' => 'v1.31.0', + 'version' => '1.31.0.0', + 'reference' => 'a3cc8b044a6ea513310cbd48ef7333b384945638', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-ctype', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'symfony/polyfill-intl-grapheme' => array( + 'pretty_version' => 'v1.31.0', + 'version' => '1.31.0.0', + 'reference' => 'b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-intl-grapheme', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'symfony/polyfill-intl-normalizer' => array( + 'pretty_version' => 'v1.31.0', + 'version' => '1.31.0.0', + 'reference' => '3833d7255cc303546435cb650316bff708a1c75c', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'symfony/polyfill-mbstring' => array( + 'pretty_version' => 'v1.31.0', + 'version' => '1.31.0.0', + 'reference' => '85181ba99b2345b0ef10ce42ecac37612d9fd341', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-mbstring', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'symfony/service-contracts' => array( + 'pretty_version' => 'v3.5.1', + 'version' => '3.5.1.0', + 'reference' => 'e53260aabf78fb3d63f8d79d69ece59f80d5eda0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/service-contracts', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'symfony/string' => array( + 'pretty_version' => 'v7.2.0', + 'version' => '7.2.0.0', + 'reference' => '446e0d146f991dde3e73f45f2c97a9faad773c82', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/string', + 'aliases' => array(), + 'dev_requirement' => false, + ), ), ); diff --git a/vendor/composer/platform_check.php b/vendor/composer/platform_check.php index 454eefd6885661cbe529cdf7113268a6af1e3140..d515d34bb4ba069fc749d51b57dab64fd978eeb1 100644 --- a/vendor/composer/platform_check.php +++ b/vendor/composer/platform_check.php @@ -4,8 +4,12 @@ $issues = array(); -if (!(PHP_VERSION_ID >= 50500)) { - $issues[] = 'Your Composer dependencies require a PHP version ">= 5.5.0". You are running ' . PHP_VERSION . '.'; +if (!(PHP_VERSION_ID >= 80200)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 8.2.0". You are running ' . PHP_VERSION . '.'; +} + +if (PHP_INT_SIZE !== 8) { + $issues[] = 'Your Composer dependencies require a 64-bit build of PHP.'; } if ($issues) { diff --git a/views/address/create.php b/views/address/create.php new file mode 100644 index 0000000000000000000000000000000000000000..4ce25c43891242d41c937fba2a27a920fbb696cf --- /dev/null +++ b/views/address/create.php @@ -0,0 +1,62 @@ +<?php + +require base_path('views/partials/header.html'); + +require base_path('views/partials/nav.html'); + + +use core\Flash; + + +if ($message = Flash::get('success')): ?> + <div class="flash-message success"> + <?= htmlspecialchars($message) ?> + </div> +<?php +elseif ($message = Flash::get('error')): ?> + <div class="flash-message error"> + <?= htmlspecialchars($message) ?> + </div> +<?php +endif; ?> +<main> + <h2>Add Address</h2> + <form action="/address/store" method="POST"> + <label for="street">Street:</label> + <input type="text" id="street" name="street" required aria-required="true"> + + <label for="zipcode">Zipcode:</label> + <input type="text" id="zipcode" name="zipcode" required aria-required="true"> + + <label for="city">City:</label> + <input type="text" id="city" name="city" required aria-required="true"> + + <label for="district">District:</label> + <input type="text" id="district" name="district" required aria-required="true"> + + <label for="country">Country:</label> + <select id="country" name="country"> + <?php + foreach ($countries as $country): ?> + <option value="<?= htmlspecialchars($country->value) ?>"><?= htmlspecialchars( + $country->name + ) ?></option> + <?php + endforeach; ?> + </select> + + <div class="category-item"> + <input type="checkbox" id="main" name="main" value="1"> + <label for="main">Main Address</label> + </div> + + <form action="/address/store" method="POST"> + <input type="hidden" name="user_id" value="<?= $user_id; ?>"> + <button type="submit">Save Address</button> + </form> + +</main> +<?php +require base_path('views/partials/footer.html'); +$user_id = filter_input(INPUT_GET, 'user_id', FILTER_SANITIZE_NUMBER_INT); +?> diff --git a/views/address/edit.php b/views/address/edit.php new file mode 100644 index 0000000000000000000000000000000000000000..cc622b636253def90383e3e4046095ca77232d24 --- /dev/null +++ b/views/address/edit.php @@ -0,0 +1,64 @@ +<?php + +require base_path('views/partials/header.html'); + +require base_path('views/partials/nav.html'); + + +use core\Flash; + + +if ($message = Flash::get('success')): ?> + <div class="flash-message success"> + <?= htmlspecialchars($message) ?> + </div> +<?php +elseif ($message = Flash::get('error')): ?> + <div class="flash-message error"> + <?= htmlspecialchars($message) ?> + </div> +<?php +endif; ?> +<main> + <h2>Edit Address</h2> + <form action="/address/update" method="POST"> + <label for="street">Street:</label> + <input type="text" id="street" name="street" value="<?= $address['street'] ?> " required aria-required="true"> + + <label for="zipcode">Zipcode:</label> + <input type="text" id="zipcode" name="zipcode" value="<?= $address['zipcode'] ?>" required aria-required="true"> + + <label for="city">City:</label> + <input type="text" id="city" name="city" value="<?= $address['city'] ?>" required aria-required="true"> + + <label for="district">District:</label> + <input type="text" id="district" name="district" value="<?= $address['district'] ?>" required + aria-required="true"> + + <label for="country">Country:</label> + <select id="country" name="country"> + <?php + foreach ($countries as $country): ?> + <option value="<?= htmlspecialchars($country->value) ?>"><?= htmlspecialchars( + $country->name + ) ?></option> + <?php + endforeach; ?> + </select> + <div class="category-item"> + <input type="checkbox" id="main" name="main" value="1" <?php + echo $address['main'] ? 'checked' : ''; ?>> + <label for="main">Main Address</label> + </div> + + <form action="/address/update" method="POST"> + <input type="hidden" name="user_id" value="<?= $user_id; ?>"> + <input type="hidden" name="addressId" value="<?= $address['id']; ?>"> + <button type="submit">Save Address</button> + </form> + +</main> +<?php +require base_path('views/partials/footer.html'); +$user_id = filter_input(INPUT_GET, 'user', FILTER_SANITIZE_NUMBER_INT); +?> diff --git a/views/address/show.php b/views/address/show.php new file mode 100644 index 0000000000000000000000000000000000000000..465c9c9656ac2b1d8492c9a09997d3bc34f4c894 --- /dev/null +++ b/views/address/show.php @@ -0,0 +1,80 @@ +<?php + +require base_path('views/partials/header.html'); +require base_path('views/partials/nav.html'); + +use core\Flash; + +if($message = Flash::get('success')): ?> +<div class ="flash-message success"> + <?= htmlspecialchars($message) ?> +</div> +<?php +elseif ($message = Flash::get('error')): ?> + <div class="flash-message error"> + <?= htmlspecialchars($message) ?> + </div> +<?php +endif; ?> + +<body> +<main class="container"> + <h1>Address Management</h1> + <table class="user-info-table"> + <thead> + <tr> + <th>ID</th> + <th>Street</th> + <th>Zip Code</th> + <th>City</th> + <th>District</th> + <th>Country</th> + <th>Main Address</th> + <th>Actions</th> + + </tr> + </thead> + <tbody> + <?php foreach ($addresses as $address): ?> + <tr> + <td><?= htmlspecialchars($address['id']); ?></td> + <td><?= htmlspecialchars($address['street']); ?></td> + <td><?= htmlspecialchars($address['zipcode']); ?></td> + <td><?= htmlspecialchars($address['city']); ?></td> + <td><?= htmlspecialchars($address['district']); ?></td> + <td><?= htmlspecialchars($address['country']); ?></td> + <td><?= $address['main'] ? 'Yes' : 'No'; ?></td> + <td> + <form action="/address/edit" method="GET" style="display:inline;"> + <input type="hidden" name="user_id" value="<?= $user_id; ?>"> + <input type="hidden" name="addressId" value="<?= $address['id']; ?>"> + <button type="submit" class="edit-button">Edit</button> + </form> + <form action="/address/destroy" method="POST" style="display:inline;"> + <input type="hidden" name="user_id" value="<?= $user_id; ?>"> + <input type="hidden" name="addressId" value="<?= $address['id']; ?>"> + <button type="submit" class="delete-button" + onclick="return confirm('Are you sure you want to delete this address?');">Delete + </button> + </form> + </td> + + + </tr> + <?php endforeach; ?> + </tbody> + </table> + <div class="/address/show"> + <form action="/address/create" method="GET" style="display:inline;"> + <input type="hidden" name="user_id" value="<?= $user_id; ?>"> + <button type="submit" class="edit-button">Add New Address</button> + </form> + + <a href="/profile/show" class="add-product-button">Back to Profile</a> + + + </div> +</main> +<?php +require base_path('views/partials/footer.html'); +?> \ No newline at end of file diff --git a/views/products/show.view.php b/views/products/show.view.php index 723d44fe39e9ddb9c6f0430dcfd6d1f7ab965d20..e7bc5a4b7767e87c463c1225e4367f2b5139f6ac 100644 --- a/views/products/show.view.php +++ b/views/products/show.view.php @@ -19,6 +19,8 @@ elseif ($message = Flash::get('error')): ?> <?php endif; ?> +<?php if($_SESSION['user']['is_admin']): ?> + <main> <h2>Products</h2> <table> @@ -56,6 +58,39 @@ endif; ?> <a href="/products/create" class="add-product-button">Add Product</a> </main> +<?php endif; + +if(!$_SESSION['user']['is_admin']):?> + +<main> + <h2>Products</h2> + <table> + <thead> + <tr> + <th style="text-align: center;">ID</th> + <th style="text-align: center;">Name</th> + <th style="text-align: center;">Price</th> + <th style="text-align: center;">Description</th> + <th style="text-align: center;">Category</th> + </tr> + </thead> + <tbody> + <?php foreach ($products as $product): ?> + <tr> + <td><?= htmlspecialchars($product['id']) ?></td> + <td><?= htmlspecialchars($product['name']) ?></td> + <td><?= htmlspecialchars($product['price']) ?>€</td> + <td><?= htmlspecialchars($product['description']) ?></td> + <td><?= htmlspecialchars($product['categories']) ?></td> + </form> + </td> + </tr> + <?php endforeach; ?> + </tbody> + </table> +</main> + +<?php endif; ?> <script src="/js/confirmDelete.js"></script> <?php diff --git a/views/profile/edit.profile.php b/views/profile/edit.profile.php index 744ca24a11ae6a056703726cd44a6c1028f3a6a2..444a6321693e2d02c12dae8f31dc6d8012ffa63a 100644 --- a/views/profile/edit.profile.php +++ b/views/profile/edit.profile.php @@ -20,12 +20,6 @@ elseif ($message = Flash::get('error')): ?> <?php endif; -$user = $_SESSION['user'] ?? null; - -if (!$user) { - header('Location: /login'); // Redirect to login if no user is found - exit; -} ?> <body> @@ -40,17 +34,17 @@ if (!$user) { <input type="text" id="email" name="email" value="<?php echo htmlspecialchars($user['email']); ?>" readonly aria-required="true"> - <label for="address">Address:</label> - <textarea id="address" name="address" required aria-required="true"><?php - echo htmlspecialchars($user['address']); ?></textarea> - + <?php if ($_SESSION['user']['email'] !== $user['email']): ?> <div class="category-item"> <input type="checkbox" id="is_admin" name="is_admin" value="1" <?php echo $user['is_admin'] ? 'checked' : ''; ?>> <label for="is_admin">Admin</label> </div> + <?php endif; ?> <button type="submit">Update Profile</button> + <input type = "hidden" name = "user_id" value = "<?= $user['id']; ?>"> + <a href="/address/show?user_id=<?= $user['id']; ?>" class="return-button">Addresses</a> </form> </main> <?php diff --git a/views/profile/profile.php b/views/profile/profile.php index 38b77c931c7b25fc695b1962d03153f209b3b15b..06ec6f7a242209e55e82f0002f0d6d923ba4f1f5 100644 --- a/views/profile/profile.php +++ b/views/profile/profile.php @@ -20,8 +20,6 @@ elseif ($message = Flash::get('error')): ?> <?php endif; -$user = $_SESSION['user'] ?? null; - if (!$user) { header('Location: /login'); // Redirect to login if no user is found exit; @@ -34,26 +32,45 @@ if (!$user) { <table> <tr> <th>Name</th> - <td><?php - echo htmlspecialchars($user['name']); ?></td> + <td><?= htmlspecialchars($user['name']); ?></td> </tr> <tr> <th>Email</th> - <td><?php - echo htmlspecialchars($user['email']); ?></td> + <td><?= htmlspecialchars($user['email']); ?></td> </tr> <tr> - <th>Address</th> - <td><?php - echo htmlspecialchars($user['address']); ?></td> + <th>Admin Status</th> + <td><?= $user['is_admin'] ? 'Yes' : 'No'; ?></td> </tr> + </table> + <table> <tr> - <th>Admin Status</th> - <td><?php - echo $user['is_admin'] ? 'Yes' : 'No'; ?></td> + <th style="text-align: center;" colspan="2">Address</th> + </tr> + <tr> + <th>Street and Number</th> + <td><?= htmlspecialchars($address['street']); ?></td> + </tr> + <tr> + <th>ZipCode</th> + <td><?= htmlspecialchars($address['zipcode']); ?></td> </tr> + <tr> + <th>City</th> + <td><?= htmlspecialchars($address['city']); ?></td> + </tr> + <tr> + <th>District</th> + <td><?= htmlspecialchars($address['district']); ?></td> + </tr> + <tr> + <th>Country</th> + <td><?= htmlspecialchars($address['country']); ?></td> + </tr> + </table> <a href="/profile/edit" class="add-product-button">Edit Profile</a> + </main> <?php require base_path('views/partials/footer.html'); ?> \ No newline at end of file diff --git a/views/users/edit.user.php b/views/users/edit.user.php index 83949713be4bb6fad9576ba4e65d68fa5cd7aa48..b49a8081837108202c8d935193037fe687c3da78 100644 --- a/views/users/edit.user.php +++ b/views/users/edit.user.php @@ -23,21 +23,23 @@ endif; ?> <main class="container"> <h2>Edit User Profile</h2> <form action="/user/update" method="POST"> - <input type="hidden" name="id" value="<?= htmlspecialchars($selectedUser['id']); ?>"> + <input type="hidden" name="id" value="<?= htmlspecialchars($user['id']); ?>"> <label for="name">Name:</label> - <input type="text" id="name" name="name" value="<?= htmlspecialchars($selectedUser['name']); ?>" required + <input type="text" id="name" name="name" value="<?= htmlspecialchars($user['name']); ?>" required aria-required="true"> <label for="email">Email:</label> - <input type="text" id="email" name="email" value="<?= htmlspecialchars($selectedUser['email']); ?>" readonly + <input type="text" id="email" name="email" value="<?= htmlspecialchars($user['email']); ?>" readonly aria-required="true"> - <label for="address">Address:</label> - <textarea id="address" name="address" required aria-required="true"><?= htmlspecialchars( - $selectedUser['address'] - ); ?></textarea> - + <?php if ($_SESSION['user']['email'] !== $user['email']): ?> + <div class="category-item"> + <input type="checkbox" id="is_admin" name="is_admin" value="1" <?php + echo $user['is_admin'] ? 'checked' : ''; ?>> + <label for="is_admin">Admin</label> + </div> + <?php endif; ?> <button type="submit">Update User</button> </form> diff --git a/views/users/register.php b/views/users/register.php index e5c1ce2253433d5af41060f16b39ad242aca8b08..6816864fcb80bb742b1f494ae8f5ac6273a56b54 100644 --- a/views/users/register.php +++ b/views/users/register.php @@ -33,14 +33,272 @@ endif; ?> <label for="password"> Confirm Password:</label> <input type="password" id="password" name="confirm_password" required aria-required="true"> - <label for="address">Address:</label> - <textarea id="address" name="address" required aria-required="true"></textarea> - <div class="category-item"> <input type="checkbox" id="is_admin" name="is_admin" value="1"> <label for="is_admin">Admin</label> </div> + <div class="address"> + <h3>Address:</h3> + + <label for = "street">Street and Apartment Number:</label> + <input type="text" id="street" name="street" required aria-required="true"> + + <label for = "zipcode">ZipCode:</label> + <input type="text" id="zipcode" name="zipcode" required aria-required="true"> + + <label for = "city">City:</label> + <input type="text" id="city" name="city" required aria-required="true"> + + <label for = "district">District:</label> + <input type="text" id="district" name="district" required aria-required="true"> + + <label for = "country">Country:</label> + <select id = "country" name ="country"> + <option value="Portugal">Portugal</option> + <option value="Afghanistan">Afghanistan</option> + <option value="Albania">Albania</option> + <option value="Algeria">Algeria</option> + <option value="American Samoa">American Samoa</option> + <option value="Andorra">Andorra</option> + <option value="Angola">Angola</option> + <option value="Anguilla">Anguilla</option> + <option value="Antartica">Antarctica</option> + <option value="Antigua and Barbuda">Antigua and Barbuda</option> + <option value="Argentina">Argentina</option> + <option value="Armenia">Armenia</option> + <option value="Aruba">Aruba</option> + <option value="Australia">Australia</option> + <option value="Austria">Austria</option> + <option value="Azerbaijan">Azerbaijan</option> + <option value="Bahamas">Bahamas</option> + <option value="Bahrain">Bahrain</option> + <option value="Bangladesh">Bangladesh</option> + <option value="Barbados">Barbados</option> + <option value="Belarus">Belarus</option> + <option value="Belgium">Belgium</option> + <option value="Belize">Belize</option> + <option value="Benin">Benin</option> + <option value="Bermuda">Bermuda</option> + <option value="Bhutan">Bhutan</option> + <option value="Bolivia">Bolivia</option> + <option value="Bosnia and Herzegowina">Bosnia and Herzegowina</option> + <option value="Botswana">Botswana</option> + <option value="Bouvet Island">Bouvet Island</option> + <option value="Brazil">Brazil</option> + <option value="British Indian Ocean Territory">British Indian Ocean Territory</option> + <option value="Brunei Darussalam">Brunei Darussalam</option> + <option value="Bulgaria">Bulgaria</option> + <option value="Burkina Faso">Burkina Faso</option> + <option value="Burundi">Burundi</option> + <option value="Cambodia">Cambodia</option> + <option value="Cameroon">Cameroon</option> + <option value="Canada">Canada</option> + <option value="Cape Verde">Cape Verde</option> + <option value="Cayman Islands">Cayman Islands</option> + <option value="Central African Republic">Central African Republic</option> + <option value="Chad">Chad</option> + <option value="Chile">Chile</option> + <option value="China">China</option> + <option value="Christmas Island">Christmas Island</option> + <option value="Cocos Islands">Cocos (Keeling) Islands</option> + <option value="Colombia">Colombia</option> + <option value="Comoros">Comoros</option> + <option value="Congo">Congo</option> + <option value="Congo">Congo, the Democratic Republic of the</option> + <option value="Cook Islands">Cook Islands</option> + <option value="Costa Rica">Costa Rica</option> + <option value="Cota D'Ivoire">Cote d'Ivoire</option> + <option value="Croatia">Croatia (Hrvatska)</option> + <option value="Cuba">Cuba</option> + <option value="Cyprus">Cyprus</option> + <option value="Czech Republic">Czech Republic</option> + <option value="Denmark">Denmark</option> + <option value="Djibouti">Djibouti</option> + <option value="Dominica">Dominica</option> + <option value="Dominican Republic">Dominican Republic</option> + <option value="East Timor">East Timor</option> + <option value="Ecuador">Ecuador</option> + <option value="Egypt">Egypt</option> + <option value="El Salvador">El Salvador</option> + <option value="Equatorial Guinea">Equatorial Guinea</option> + <option value="Eritrea">Eritrea</option> + <option value="Estonia">Estonia</option> + <option value="Ethiopia">Ethiopia</option> + <option value="Falkland Islands">Falkland Islands (Malvinas)</option> + <option value="Faroe Islands">Faroe Islands</option> + <option value="Fiji">Fiji</option> + <option value="Finland">Finland</option> + <option value="France">France</option> + <option value="France Metropolitan">France, Metropolitan</option> + <option value="French Guiana">French Guiana</option> + <option value="French Polynesia">French Polynesia</option> + <option value="French Southern Territories">French Southern Territories</option> + <option value="Gabon">Gabon</option> + <option value="Gambia">Gambia</option> + <option value="Georgia">Georgia</option> + <option value="Germany">Germany</option> + <option value="Ghana">Ghana</option> + <option value="Gibraltar">Gibraltar</option> + <option value="Greece">Greece</option> + <option value="Greenland">Greenland</option> + <option value="Grenada">Grenada</option> + <option value="Guadeloupe">Guadeloupe</option> + <option value="Guam">Guam</option> + <option value="Guatemala">Guatemala</option> + <option value="Guinea">Guinea</option> + <option value="Guinea-Bissau">Guinea-Bissau</option> + <option value="Guyana">Guyana</option> + <option value="Haiti">Haiti</option> + <option value="Heard and McDonald Islands">Heard and Mc Donald Islands</option> + <option value="Holy See">Holy See (Vatican City State)</option> + <option value="Honduras">Honduras</option> + <option value="Hong Kong">Hong Kong</option> + <option value="Hungary">Hungary</option> + <option value="Iceland">Iceland</option> + <option value="India">India</option> + <option value="Indonesia">Indonesia</option> + <option value="Iran">Iran (Islamic Republic of)</option> + <option value="Iraq">Iraq</option> + <option value="Ireland">Ireland</option> + <option value="Israel">Israel</option> + <option value="Italy">Italy</option> + <option value="Jamaica">Jamaica</option> + <option value="Japan">Japan</option> + <option value="Jordan">Jordan</option> + <option value="Kazakhstan">Kazakhstan</option> + <option value="Kenya">Kenya</option> + <option value="Kiribati">Kiribati</option> + <option value="Democratic People's Republic of Korea">Korea, Democratic People's Republic of</option> + <option value="Korea">Korea, Republic of</option> + <option value="Kuwait">Kuwait</option> + <option value="Kyrgyzstan">Kyrgyzstan</option> + <option value="Lao">Lao People's Democratic Republic</option> + <option value="Latvia">Latvia</option> + <option value="Lebanon">Lebanon</option> + <option value="Lesotho">Lesotho</option> + <option value="Liberia">Liberia</option> + <option value="Libyan Arab Jamahiriya">Libyan Arab Jamahiriya</option> + <option value="Liechtenstein">Liechtenstein</option> + <option value="Lithuania">Lithuania</option> + <option value="Luxembourg">Luxembourg</option> + <option value="Macau">Macau</option> + <option value="Macedonia">Macedonia, The Former Yugoslav Republic of</option> + <option value="Madagascar">Madagascar</option> + <option value="Malawi">Malawi</option> + <option value="Malaysia">Malaysia</option> + <option value="Maldives">Maldives</option> + <option value="Mali">Mali</option> + <option value="Malta">Malta</option> + <option value="Marshall Islands">Marshall Islands</option> + <option value="Martinique">Martinique</option> + <option value="Mauritania">Mauritania</option> + <option value="Mauritius">Mauritius</option> + <option value="Mayotte">Mayotte</option> + <option value="Mexico">Mexico</option> + <option value="Micronesia">Micronesia, Federated States of</option> + <option value="Moldova">Moldova, Republic of</option> + <option value="Monaco">Monaco</option> + <option value="Mongolia">Mongolia</option> + <option value="Montserrat">Montserrat</option> + <option value="Morocco">Morocco</option> + <option value="Mozambique">Mozambique</option> + <option value="Myanmar">Myanmar</option> + <option value="Namibia">Namibia</option> + <option value="Nauru">Nauru</option> + <option value="Nepal">Nepal</option> + <option value="Netherlands">Netherlands</option> + <option value="Netherlands Antilles">Netherlands Antilles</option> + <option value="New Caledonia">New Caledonia</option> + <option value="New Zealand">New Zealand</option> + <option value="Nicaragua">Nicaragua</option> + <option value="Niger">Niger</option> + <option value="Nigeria">Nigeria</option> + <option value="Niue">Niue</option> + <option value="Norfolk Island">Norfolk Island</option> + <option value="Northern Mariana Islands">Northern Mariana Islands</option> + <option value="Norway">Norway</option> + <option value="Oman">Oman</option> + <option value="Pakistan">Pakistan</option> + <option value="Palau">Palau</option> + <option value="Panama">Panama</option> + <option value="Papua New Guinea">Papua New Guinea</option> + <option value="Paraguay">Paraguay</option> + <option value="Peru">Peru</option> + <option value="Philippines">Philippines</option> + <option value="Pitcairn">Pitcairn</option> + <option value="Poland">Poland</option> + <option value="Puerto Rico">Puerto Rico</option> + <option value="Qatar">Qatar</option> + <option value="Reunion">Reunion</option> + <option value="Romania">Romania</option> + <option value="Russia">Russian Federation</option> + <option value="Rwanda">Rwanda</option> + <option value="Saint Kitts and Nevis">Saint Kitts and Nevis</option> + <option value="Saint Lucia">Saint LUCIA</option> + <option value="Saint Vincent">Saint Vincent and the Grenadines</option> + <option value="Samoa">Samoa</option> + <option value="San Marino">San Marino</option> + <option value="Sao Tome and Principe">Sao Tome and Principe</option> + <option value="Saudi Arabia">Saudi Arabia</option> + <option value="Senegal">Senegal</option> + <option value="Seychelles">Seychelles</option> + <option value="Sierra">Sierra Leone</option> + <option value="Singapore">Singapore</option> + <option value="Slovakia">Slovakia (Slovak Republic)</option> + <option value="Slovenia">Slovenia</option> + <option value="Solomon Islands">Solomon Islands</option> + <option value="Somalia">Somalia</option> + <option value="South Africa">South Africa</option> + <option value="South Georgia">South Georgia and the South Sandwich Islands</option> + <option value="Span">Spain</option> + <option value="Sri Lanka">Sri Lanka</option> + <option value="St. Helena">St. Helena</option> + <option value="St. Pierre and Miguelon">St. Pierre and Miquelon</option> + <option value="Sudan">Sudan</option> + <option value="Suriname">Suriname</option> + <option value="Svalbard">Svalbard and Jan Mayen Islands</option> + <option value="Swaziland">Swaziland</option> + <option value="Sweden">Sweden</option> + <option value="Switzerland">Switzerland</option> + <option value="Syria">Syrian Arab Republic</option> + <option value="Taiwan">Taiwan, Province of China</option> + <option value="Tajikistan">Tajikistan</option> + <option value="Tanzania">Tanzania, United Republic of</option> + <option value="Thailand">Thailand</option> + <option value="Togo">Togo</option> + <option value="Tokelau">Tokelau</option> + <option value="Tonga">Tonga</option> + <option value="Trinidad and Tobago">Trinidad and Tobago</option> + <option value="Tunisia">Tunisia</option> + <option value="Turkey">Turkey</option> + <option value="Turkmenistan">Turkmenistan</option> + <option value="Turks and Caicos">Turks and Caicos Islands</option> + <option value="Tuvalu">Tuvalu</option> + <option value="Uganda">Uganda</option> + <option value="Ukraine">Ukraine</option> + <option value="United Arab Emirates">United Arab Emirates</option> + <option value="United Kingdom">United Kingdom</option> + <option value="United States of America">United States of America</option> + <option value="United States Minor Outlying Islands">United States Minor Outlying Islands</option> + <option value="Uruguay">Uruguay</option> + <option value="Uzbekistan">Uzbekistan</option> + <option value="Vanuatu">Vanuatu</option> + <option value="Venezuela">Venezuela</option> + <option value="Vietnam">Viet Nam</option> + <option value="Virgin Islands (British)">Virgin Islands (British)</option> + <option value="Virgin Islands (U.S)">Virgin Islands (U.S.)</option> + <option value="Wallis and Futana Islands">Wallis and Futuna Islands</option> + <option value="Western Sahara">Western Sahara</option> + <option value="Yemen">Yemen</option> + <option value="Serbia">Serbia</option> + <option value="Zambia">Zambia</option> + <option value="Zimbabwe">Zimbabwe</option> + </select> + + + + </div> <button type="submit">Register</button> diff --git a/views/users/show.all.php b/views/users/show.all.php index 4a438d3b6f61cf9f7c3bc032df4612e5b3308916..f19ce29f1801440c2ddeae76ce19469f50e7b00b 100644 --- a/views/users/show.all.php +++ b/views/users/show.all.php @@ -27,7 +27,7 @@ endif; ?> <th>ID</th> <th>Name</th> <th>Email</th> - <th>Address</th> + <th>Main Address</th> <th>Admin Status</th> <th>Actions</th> </tr> @@ -36,10 +36,17 @@ endif; ?> <?php foreach ($users as $user): ?> <tr> + <?php if (isset($user['main']) && $user['main'] == 1): ?> <td><?= htmlspecialchars($user['id']); ?></td> <td><?= htmlspecialchars($user['name']); ?></td> <td><?= htmlspecialchars($user['email']); ?></td> - <td><?= htmlspecialchars($user['address']); ?></td> + <td> + <div class="address-line"><?= htmlspecialchars($user['street']); ?></div> + <div class="address-line"><?= htmlspecialchars($user['zipcode']); ?></div> + <div class="address-line"><?= htmlspecialchars($user['city']); ?></div> + <div class="address-line"><?= htmlspecialchars($user['district']); ?></div> + <div class="address-line"><?= htmlspecialchars($user['country']); ?></div> + </td> <td><?= $user['is_admin'] ? 'Yes' : 'No'; ?></td> <td> <form action="/user/edit" method="GET" style="display:inline;"> @@ -52,7 +59,12 @@ endif; ?> onclick="return confirm('Are you sure you want to delete this user?');">Delete </button> </form> + <form action="/address/show" method="GET" style="display:inline;"> + <input type="hidden" name="user_id" value="<?= $user['id']; ?>"> + <button type="submit" class="delete-button">Address</button> + </form> </td> + <?php endif; ?> </tr> <?php endforeach; ?> diff --git a/web/routes.php b/web/routes.php index f058bd4c469d710dde5703629b71693c800494c5..559b4bf4653379ef18851642687f5ad5706b0349 100644 --- a/web/routes.php +++ b/web/routes.php @@ -5,6 +5,7 @@ use controllers\ProductController; use controllers\UserController; use controllers\LoginController; use controllers\ProfileController; +use controllers\AddressController; //Home $router->get('/', HomeController::class, 'index' ); @@ -12,11 +13,11 @@ $router->get('/home/index', HomeController::class, 'index'); //Products $router->get('/products/index', ProductController::class, 'index' )->only('auth'); -$router->post('/products/store', ProductController::class, 'store')->only('auth'); -$router->get('/products/create', ProductController::class, 'create')->only('auth'); -$router->get('/products/edit', ProductController::class, 'edit')->only('auth'); -$router->post('/products/update', ProductController::class, 'update')->only('auth'); -$router->post('/products/destroy', ProductController::class, 'destroy')->only('auth'); +$router->post('/products/store', ProductController::class, 'store')->only('admin'); +$router->get('/products/create', ProductController::class, 'create')->only('admin'); +$router->get('/products/edit', ProductController::class, 'edit')->only('admin'); +$router->post('/products/update', ProductController::class, 'update')->only('admin'); +$router->post('/products/destroy', ProductController::class, 'destroy')->only('admin'); //Login $router->get('/login', LoginController::class, 'showLogin')->only('guest'); @@ -39,3 +40,11 @@ $router->get('/user/index', UserController::class, 'index')->only('admin'); $router->get('/user/edit', UserController::class, 'edit')->only('admin'); $router->post('/user/update', UserController::class, 'update')->only('admin'); $router->post('/user/destroy', UserController::class, 'destroy')->only('admin'); + +//Address +$router->get('/address/show', AddressController::class, 'show')->only('auth'); +$router->get('/address/create', AddressController::class, 'create')->only('auth'); +$router->post('/address/store', AddressController::class, 'store')->only('auth'); +$router->post('/address/destroy', AddressController::class, 'destroy')->only('auth'); +$router->get('/address/edit', AddressController::class, 'edit')->only('auth'); +$router->post('/address/update', AddressController::class, 'update')->only('auth'); \ No newline at end of file