AoE Tournament Elo (Documentation)
🌐 Hosted here: https://aoe-elo.com (still the old backend)
🗨 Discord: https://discord.gg/hZzheB2kVE
License
The aoe-elo design documentation is open-source and licensed under the GNU Free Documentation License v1.3 or later.
Contributing
Protocols
2023-08-07 - Voice talk with @ghostmonkey
- simonsan explained a few things regarding the backend, so it’s easier to
determine how the Request-Business Logic-Response cycle works in it
- basically web requests land in
Routes/web.php
, API requests land inRoutes/api.php
then you either use aController
inapp/http/Controllers/web/{e.g. PageController}
to create a response in a method of that class or directly form a response in the corresponding function in{web,api}.php
- basically web requests land in
- simonsan talked about how data can be transferred to a svelte file (from my
current knowledge, that might change as I’m diving deeper into
laravel
andinertia
) - we talked about possible current and future use cases
- simonsan sent
players.json
,teams.json
, andtournaments.json
response to work with for drafting on thefrontend
side - agreed on work split, so simonsan will work on the backend for now
implementing/refactoring/migrating all the current existing business logic and
ghostmonkey will continue to design the frontend
- we talked about who has the final say on what is being done, and we assumed that we will all work together, waiting for feedback of participants in @here and go forward from there. in the end new things need to get used to, it’s normal.
Design
Personas
-
Victor “Veteran” Sanchez
- Age: 30
- Background: A long-time fan of AoE II since its early days. Victor has been attending and watching tournaments for over a decade.
- Goal: Wants to track how his favorite old-time players have evolved in rating over the years and reminisce about historical matches.
- Behavior: Often discusses past matches and players in online forums.
-
Lila “Newbie” Foster
- Age: 19
- Background: Recently introduced to the competitive scene of AoE II and is eager to learn about the top players and teams.
- Goal: Wants an easy-to-understand overview of player ratings and their significance.
- Behavior: Frequently searches for recent tournament results and player highlights.
-
Raj “Analyst” Mehta
- Age: 28
- Background: A professional esports analyst who dives deep into match statistics to provide commentary and insights.
- Goal: Needs detailed player performance metrics and rating histories for his analysis.
- Behavior: Uses multiple platforms to gather game data and statistics.
-
Elena “Team Manager” Vasquez
- Age: 34
- Background: Manages a competitive AoE II team.
- Goal: Wants to scout potential talents and keep an eye on the competition.
- Behavior: Regularly checks player profiles, especially rising stars.
-
Mike “Casual Gamer” O’Donnell
- Age: 24
- Background: Enjoys playing AoE II casually and occasionally watches tournaments.
- Goal: Curious about the best players and their strategies.
- Behavior: Sporadically visits the webapp after major tournaments.
-
Sophia “Journalist” Lee
- Age: 27
- Background: An esports journalist covering major tournaments and player stories.
- Goal: Needs up-to-date player ratings and profiles for her articles.
- Behavior: Frequently uses the webapp for references when writing or reporting.
-
Hassan “Streamer” Farid
- Age: 22
- Background: Popular AoE II streamer who interacts with his fans about the competitive scene.
- Goal: To stay informed about the competitive landscape and engage his viewers with relevant discussions.
- Behavior: References the webapp during his live streams.
-
Lucia “History Buff” Martinez
- Age: 40
- Background: Enthusiastic about the history of games and esports.
- Goal: Enjoys diving deep into the historical aspect of the competitive AoE II scene.
- Behavior: Spends time exploring past tournaments and player careers.
-
Danielle “Organizer” Thompson
- Age: 32
- Background: A dedicated organizer of local and international AoE II tournaments. Danielle has been orchestrating gaming events for the past 7 years and has a great network within the AoE II community.
- Goal: Wants to keep track of top players to invite for exhibition matches and to ensure fair seeding in her tournaments. Also wishes to monitor emerging talents to offer opportunities in her next events.
- Behavior: Frequently checks the webapp for the latest player ratings, particularly after major tournaments, to make informed decisions about invitations and seeding. Additionally, she cross-references player performance metrics with their historical game records to anticipate potential fan-favorite matchups.
- Additional Needs: A tool within the webapp that allows her to simulate tournament brackets based on current ratings, or an API she can connect with her own tournament management system.
-
Jin “BladeMaster” Kim
- Age: 26
- Background: A professional AoE II player from South Korea, Jin has been playing since he was a teenager. He has won multiple regional tournaments and has recently made a significant impact on the international scene. He practices for hours daily and is a key player in his team.
- Goal: Wants to monitor his own ratings and compare with other top players. Also interested in revisiting his past matches to learn from them and scout his potential competitors in upcoming tournaments.
- Behavior: Checks the webapp frequently, especially after a tournament he participated in, to see how his performance affected his rating. He also watches replays of his own games and those of top competitors to analyze strategies.
- Additional Needs: A detailed breakdown of his performance metrics over time. Access to match replays linked directly from the webapp. Notifications or alerts when significant rating changes occur or when new tournaments are added.
Use cases
-
Victor “Veteran” Sanchez
- View historical matches and player profiles.
- Search for old-time players and their ratings.
-
Lila “Newbie” Foster
- View the current top players and their ratings.
- Access a beginner’s guide or glossary on rating systems.
-
Raj “Analyst” Mehta
- Dive deep into player performance metrics.
- Access detailed match statistics.
-
Elena “Team Manager” Vasquez
- View player profiles with an emphasis on recent performance.
- Scout new talent based on performance trends.
-
Mike “Casual Gamer” O’Donnell
- Check summaries of recent major tournaments.
- Browse player highlights and strategies.
-
Sophia “Journalist” Lee
- Access up-to-date player ratings.
- Extract quotes or notable achievements for articles.
-
Hassan “Streamer” Farid
- Check real-time competitive landscape updates.
- Engage viewers with player rating discussions.
-
Lucia “History Buff” Martinez
- Explore archived tournaments.
- Track long-term player career arcs.
- Annual breakdown, letting me see how active and competitive a player was in any given year
- I want a user-friendly search function so I can quickly find tournaments from the past.
- I’d like to pick two or more players and juxtapose their stats side by side
- Being able to track and compare the ELO progression of players will let me analyze patterns and significant moments in their careers.
- I want to be able to search and study retired players too.
- When viewing a player’s profile, seeing a neat and organized display of their victories and milestones will help me appreciate their journey.
- It’s not always about the money, but it’s interesting to see the financial rewards players have garnered over the years.
- Knowing who a player’s main rivals were and who they consistently struggled against or triumphed over tells a more vivid story.
- I want to understand a player’s affiliations and the teams they’ve played with, offering another dimension to their competitive journey.
- Having direct links to game footage lets me study and appreciate gameplay, especially from significant or iconic matches.
-
Danielle “Organizer” Thompson
- Access up-to-date player ratings for seeding.
- Simulate tournament brackets.
- Monitor emerging talents for invitations.
-
Jin “BladeMaster” Kim
- Jin wants to check his current rating and see how it has changed after a recent tournament.
- Jin is interested in comparing his rating with other top players to gauge his position in the competitive scene.
- Jin wants to receive alerts or notifications when his rating changes or when there’s an update about a new tournament.
User stories
These are written from the perspective of the user, detailing what they want to achieve.
-
Victor “Veteran” Sanchez
- As a long-time fan, I want to view historical matches so that I can reminisce about the past.
- As a fan, I want to search for old-time players and see their ratings to see how they have evolved.
-
Lila “Newbie” Foster
- As a new fan, I want to see the top players and understand the rating system so that I can follow the competitive scene.
-
Raj “Analyst” Mehta
- As an analyst, I want detailed player metrics so I can provide in-depth commentary.
- As an analyst, I need to access detailed match statistics to better understand player decisions.
-
Elena “Team Manager” Vasquez
- As a team manager, I want to view up-and-coming player profiles so I can scout potential talents.
- As a manager, I need to keep an eye on competition and their performance trends to strategize for upcoming matches.
-
Mike “Casual Gamer” O’Donnell
- As a casual gamer, I want to quickly check major tournament summaries to stay updated.
- As a gamer, I’m interested in viewing player highlights and strategies to improve my gameplay.
-
Sophia “Journalist” Lee
- As a journalist, I need up-to-date player ratings to inform my readers.
- As a journalist, I want to extract notable achievements to enrich my articles.
-
Hassan “Streamer” Farid
- As a streamer, I want real-time updates on the competitive scene to discuss with my viewers.
- As a content creator, I need to engage my viewers with discussions on player ratings and their significance.
-
Lucia “History Buff” Martinez
- As a history enthusiast, I want to delve into archived tournaments to understand the game’s evolution.
- As a fan, I’m interested in tracking long-term player careers to see how players have grown.
- As a history enthusiast, I want to effortlessly locate and access information about both recent and older tournaments to deepen my understanding of competitive gameplay history.
- As a history enthusiast, I desire tools to juxtapose multiple players’ stats, achievements, and career trajectories so I can analyze their relative performances over time.
- As a history enthusiast, I want to explore the historical ELO variations of players, comparing them when necessary, to understand their growth and competitive peaks.
- As a history enthusiast, I’d like to be able to search for retired players, ensuring that the entirety of competitive history is at my fingertips.
- As a history enthusiast, I wish to see a player’s list of achievements or tournament victories in an organized, visually appealing format to appreciate their milestones.
- As a history enthusiast, I’m interested in viewing the number of games players have played annually to understand their activity and dedication levels over the years.
- As a history enthusiast, I’d like to gauge players’ yearly earnings, offering me a financial perspective of their competitive journey.
- As a history enthusiast, I want insights on players’ best and worst historical opponents to unravel player rivalries and nemesis stories.
- As a history enthusiast, I’m keen to trace the teams players have been a part of throughout their careers, showcasing their affiliations and loyalties.
- As a history enthusiast, I want direct access to game recordings or video links, allowing me to relive iconic matches and study player techniques.
-
Danielle “Organizer” Thompson
- As an organizer, I need up-to-date player ratings to seed players fairly in my tournaments.
- As an event host, I want to simulate tournament brackets based on current ratings to plan events.
- As an organizer, I’m interested in monitoring emerging talents to offer them opportunities in my events.
-
Jin “BladeMaster” Kim
- As a professional player, I want to see my current rating so that I understand my standing in the competitive scene.
- As Jin, I’d like to compare my rating with other top players to know who my closest competitors are.
- As Jin, I want to view the profiles of potential competitors in upcoming tournaments to understand their playstyle and tactics.
- As a professional player, I’m interested in identifying emerging talents to anticipate new strategies or challenges.
- As Jin, I want to receive notifications when my rating changes to stay updated on my progress.
Requirements
Functional Requirements
-
Detailed Player Profiles and Ratings:
- Ability to view current and historical player ratings.
- Access to player profiles that show their achievements, match history, strategies, and other relevant information.
- Players’ ratings must be updated in real-time after every tournament game.
- A section dedicated to a player’s personal ratings and historical performance, including a graph/chart showcasing the progression of their ratings over time. An interactive timeline or graph displaying the historical ELO ratings of players.
- A feature allowing the juxtaposition of multiple players’ ELO ratings on the same graph.
- A visually appealing and organized section dedicated to a player’s achievements and tournament victories on their profile.
- Chronological listings and possible filter options (e.g., by significance or prize money).
- A breakdown of a player’s annual game statistics available in their profile.
- Visualization options such as pie charts or bar graphs for better comprehension.
- A yearly breakdown of players’ earnings, which can be displayed in their profiles.
- Visual tools, like a line graph, to track and compare players’ earnings over the years.
- A dedicated section on player profiles that lists all the teams they’ve been affiliated with throughout their careers.
- Chronological listings, perhaps with dates of joining and departure for each team.
-
Tournament Data:
- Archive of historical tournaments, including results, participating players, and major highlights.
- Summary and detailed view of recent tournaments.
- Real-time updates during ongoing tournaments.
-
Player Comparison Tools:
- A feature allowing users to select multiple players and view their stats in a comparative manner.
- Visual representation tools such as side-by-side graphs, charts, or tables to enhance clarity.
-
Search Functionality:
- Allow users to search for players, teams, and specific tournaments.
- Provide filters for time periods, player ratings, and other relevant criteria.
- A comprehensive, user-friendly search function that indexes both recent and older tournaments.
- Filters and sort options to narrow down search results based on time, players, regions, etc.
- A dedicated search tag or filter for retired players.
- Full profiles for retired players, including stats, achievements, and relevant history.
-
Performance Metrics and Analytics:
- Detailed statistics on player performance in individual matches.
- Performance trend graphs and charts over specified periods.
- Provide a glossary or guide explaining the metrics for newcomers.
-
Content for Newbies:
- Beginner’s guide on the rating system and how it works.
- Highlights or summary sections for quick catch-ups on the competitive scene.
-
Interactive Tools:
- Simulation tools for tournament organizers to create brackets based on player ratings.
-
API Integration:
- For external tools used by analysts, journalists, or streamers.
-
User Engagement:
- Integration with social media for sharing and discussions.
-
Scouting Tools:
- Highlight emerging talents based on performance trends for team managers and tournament organizers.
- Provide detailed profiles of new players on the scene.
-
Competitor Scouting:
- Advanced search and filtering options to view profiles and past matches of potential competitors.
- Highlight sections showcasing emerging players based on recent performance or rapid rating improvements.
- A section or tool in player profiles to highlight their historical performance against specific opponents.
- Stats like win-loss ratios, significant matches, and notable moments against those opponents.
-
Game Recordings & VoDs:
- A section in match summaries or player profiles linking to game recordings or video-on-demand (VoDs) of matches.
- Clear labeling for ease of access (e.g., “Grand Final Match 1 - VoD”).
Non-Functional Requirements
-
Performance:
- The system should provide fast response times, ensuring users can access data quickly and efficiently.
-
Usability:
- The interface should be user-friendly and intuitive, catering to both seasoned fans and newcomers.
- A clean design with clear navigation and logically grouped functionalities.
- An easily accessible profile dashboard where players can quickly see their stats, rating, and recent matches.
-
Scalability:
- As the number of tournaments, players, and users grows, the system should handle the increased load without degrading performance.
-
Security:
- User data protection should be paramount. Implement encryption and other security measures.
- Ensure data integrity for player ratings and historical records.
- Ensure that players’ personal data and statistics are kept private and are not accessible without proper permissions.
-
Availability:
- The system should be available 24/7, especially during major tournaments, with minimal downtime.
-
Accessibility:
- The platform should be accessible to users with disabilities, following the Web Content Accessibility Guidelines (WCAG).
- The app in general and statistical graphs/charts should be optimized for viewing on various devices.
-
Mobile Responsiveness:
- The system should be easily accessible and usable on various devices, including desktops, tablets, and mobile phones.
-
Backup and Recovery:
- Regular backups of data to prevent any loss.
- Efficient recovery mechanisms in case of failures.
-
Notification System:
- Reliable and timely delivery of notifications to ensure players are always updated.
- A quiet mode or “Do Not Disturb” setting for players to mute notifications during practice sessions or tournaments.
Dashboard - Personas
-
Lucia “AdminLuce” Moreno
- Role: Administrator
- Background: Works directly with the webapp developers and is responsible for overall data integrity and user management.
- Permissions: Full CRUD (Create, Read, Update, Delete) permissions. Can grant or revoke permissions to other users.
-
Henry “QuickPen” Foster
- Role: Editor
- Background: Journalist with expertise in the AoE II scene. Responsible for adding articles, editing player profiles, and ensuring the accuracy of content.
- Permissions: Read, Update, and Create. Can’t delete but can suggest removals. Can review guest user suggestions.
-
Aisha “EventQueen” Khan
- Role: Tournament Organizer
- Background: Organizes local and international AoE II tournaments. Uses the dashboard to update tournament details and results.
- Permissions: Read and Create, specifically for tournaments. Can also review guest suggestions related to tournaments.
-
Jin “BladeMaster” Kim
- Role: Competitive Player (as described previously)
- Permissions: Read and limited Update (specifically for their own profile details and perhaps personal match comments).
-
Automated Data Aggregator (ADA)
- Role: Data Collection System
- Background: A script or bot that pulls data from third-party websites. It’s non-human but integral to the system.
- Permissions: Create suggestions that need review by Admins or Editors.
Dashboard - Use Cases
-
User Authentication and Management:
- This use case ensures that users can securely log into the dashboard and be assigned specific roles and permissions. It guarantees that content and data can only be accessed and modified by authorized individuals.
-
Content Creation and Editing:
- Here, the focus is on enabling users to produce, modify, or remove content within the dashboard. This might range from updating match results, creating articles, to adding notes on game strategies.
-
Reviewing Guest & ADA Suggestions:
- Given the app’s openness to data suggestions from guest users and automated systems, this use case emphasizes reviewing, validating, and integrating this new data to maintain accuracy and relevance.
-
Data Deletion & Archiving:
- This use case revolves around the organized removal or storage of outdated or irrelevant data. It ensures the dashboard remains efficient and uncluttered while still preserving historical data.
-
Automated Data Aggregation:
- Here, the dashboard retrieves and integrates data from third-party sources automatically. This mechanism should be accurate, timely, and reduce the manual labor of data entry.
-
Feedback & Alerts:
- A system for users to receive notifications about specific events or changes. This ensures users are always updated and can act promptly when required.
Dashboard - User Stories
-
User Authentication and Management:
- As Lucia (Admin), I want to manage user permissions so I can ensure only the right people can modify data.
- As Henry (Editor), I need to log in securely to access the dashboard and perform my editing tasks.
- As Aisha (Tournament Organizer), I want to easily navigate to the tournament section after authentication to update relevant data.
- As Jin (Competitive Player), I’d like to securely view sections relevant to me without changing data accidentally.
-
Content Creation and Editing:
- As Henry (Editor), I want to add articles, news, or updates about recent or upcoming events.
- As Jin (Competitive Player), I’d like to add comments or notes to my matches to share insights or clarify specific moments.
- As Aisha (Tournament Organizer), I need to add new tournaments, specify participating players, and update match outcomes.
-
Reviewing Guest & ADA Suggestions:
- As Lucia (Admin), I want to review data suggestions to ensure the platform’s data integrity.
- As Henry (Editor), I’d like to approve or reject content suggestions from guest users or ADA to maintain content accuracy.
- As Aisha (Tournament Organizer), I want to quickly review and accept/reject tournament-related data suggestions.
-
Data Deletion & Archiving:
- As Lucia (Admin), I need the ability to remove inaccurate or outdated data to keep the platform reliable.
- As Henry (Editor), I want to suggest data removals, especially if they relate to articles or outdated news.
-
Automated Data Aggregation:
- As Lucia (Admin), I want to oversee ADA’s functioning to ensure it’s fetching accurate data.
- As Henry (Editor), I need to validate the data ADA brings in, making sure it aligns with our platform’s standards.
-
Feedback & Alerts:
- As Jin (Competitive Player), I want notifications if any changes are made to my profile or match data.
- As Aisha (Tournament Organizer), I’d like to get alerts when new tournaments are suggested by guests or ADA for validation.
Dashboard - Requirements
Functional Requirements
-
User Authentication:
- A secure authentication mechanism that supports multiple user roles (Admin, Editor, Tournament Organizer, Competitive Player).
- A session management system to maintain a user’s active session after login and automatically log out users after prolonged inactivity.
-
User Management:
- A user management interface for admins to add, modify, or remove users and adjust their permissions.
- Visibility settings to ensure users only see content and sections relevant to their permissions.
-
Content Creation & Editing:
- A rich text editor to support content creation and modification.
- Interfaces for adding and editing tournaments, player profiles, matches, and other relevant content.
- Fields to add notes or comments on matches by competitive players.
-
Review System:
- A queue or listing of data suggestions made by guest users and ADA for review.
- Functions to approve, reject, or modify these suggestions.
- Automated notifications/alerts to the relevant users when there’s a new suggestion to review.
-
Data Archival & Deletion:
- Mechanisms to soft-delete data, making it invisible to regular users but retrievable if necessary.
- An archival system to move outdated content into a less immediately accessible storage, ensuring the main dashboard remains fast and uncluttered.
-
Automated Data Aggregation:
- Integration capabilities to pull data from third-party sites through APIs or web scraping techniques.
- Settings or configurations to dictate how frequently this aggregation occurs.
-
Notifications & Alerts System:
- Real-time notifications within the dashboard for events like data changes, new reviews, etc.
- Email alerts or other external notifications to inform users of significant events or tasks.
Non-Functional Requirements
-
Performance:
- The dashboard should load and respond quickly to user actions.
- Data aggregations or other intensive tasks should not hamper the user experience.
-
Usability:
- Intuitive UI and UX design ensuring users can navigate and complete tasks easily.
- Tooltips, help sections, or user guides to assist in user orientation.
-
Security:
- Secure storage and transmission of user data, especially passwords.
- Regular security audits and vulnerability assessments.
- Implementing modern security practices like two-factor authentication.
-
Reliability:
- Regular backups to prevent data loss.
- Uptime guarantees, with minimal outages or maintenance periods.
-
Scalability:
- The ability to accommodate an increasing number of users or data volume without sacrificing performance.
-
Integration:
- Seamless connection between the dashboard and the main rating app, ensuring data consistency.
-
Accessibility:
- The dashboard should be accessible across various devices (desktop, tablet, mobile).
- Implementation of accessibility best practices to cater to users with disabilities.
Authentication
First Login
Merge Social logins
Review States (Minimal)
Development
Frequently Asked Questions (FAQ)
cURL error 60: SSL certificate problem: unable to get local issuer certificate
error
Check this answer: https://stackoverflow.com/a/34883260
- basically run
scoop install curl
then set <scoop_directory>
to the path where scoop
is installed in your
php.ini
like the following:
[curl]
; A default value for the CURLOPT_CAINFO option. This is required to be an
; absolute path.
curl.cainfo = "<scoop_directory>\apps\curl\current\bin\curl-ca-bundle.crt"
[openssl]
; The location of a Certificate Authority (CA) file on the local filesystem
; to use when verifying the identity of SSL/TLS peers. Most users should
; not specify a value for this directive as PHP will attempt to use the
; OS-managed cert stores in its absence. If specified, this value may still
; be overridden on a per-stream basis via the "cafile" SSL stream context
; option.
openssl.cafile="<scoop_directory>\apps\curl\current\bin\curl-ca-bundle.crt"
Where does the data exchange happen between front- and backend?
There are some places where data might be leaving the PHP side:
Either directly in the routes
:
-
/routes/api.php
our public API for people to query data. -
/routes/web.php
our internal API for the frontend to query data.
Data is given to the frontend via the
Web-Controllers
.
Backend development
Check out a general overview here Directory Structure. The following document assumes you have knowledge about the general Laravel-framework structure.
Code map
app/Http/Controllers
Most route
development happens here, see in the
official docs.
app/legacy
Legacy code from the old PHP-Backend you will find here. We will migrate it piece by piece.
app/Repositories
We apply the
Repository pattern.
This means, that we wrap the Eloquent models
with a repository Interface
(for easy mocking/testing) in app\Interfaces
, then we
implement that interface for the repository.
Afterwards we register both in
app\Providers\RepositoryServiceProvider.php
.
Then we use the repository interface
in the controllers’ constructor, e.g. for
the
TournamentRepositoryInterface
we add to the
TournamentController
:
private TournamentRepositoryInterface $tournamentRepository;
public function __construct(TournamentRepositoryInterface $tournamentRepository)
{
$this->tournamentRepository = $tournamentRepository;
}
app/Models
Generated models to be used with the SQLite database can be found here.
The
Legacy
subdirectory contains models to be used with thelegacy_
tables.
routes/
routes/api.php
Contains our public and internal (authenticated) API endpoints
routes/web.php
Contains endpoints to be used with inertia::render
or corresponding
controllers that return intertia
rendered pages. These routes exchange data
with the frontend.
At its core, Inertia is essentially a client-side routing library. It allows you to make page visits without forcing a full page reload. This is done using the component, a light-weight wrapper around a normal anchor link. When you click an Inertia link, Inertia intercepts the click and makes the visit via XHR instead. You can even make these visits programmatically in JavaScript using router.visit().
When Inertia makes an XHR visit, the server detects that it’s an Inertia visit and, instead of returning a full HTML response, it returns a JSON response with the JavaScript page component name and data (props). Inertia then dynamically swaps out the previous page component with the new page component and updates the browser’s history state. - Source
Porting notes
Player/PlayerCache
- players are marked inactive after 6 months from their last tournament match
- players are marked retired after 12 months from their last tournament match
Directory Structure
taken from https://github.com/laravel/docs/blob/10.x/structure.md and copied here for convenience, take a look for the actual documentation for links to work: https://laravel.com/docs/10.x/structure
Introduction
The default Laravel application structure is intended to provide a great starting point for both large and small applications. But you are free to organize your application however you like. Laravel imposes almost no restrictions on where any given class is located - as long as Composer can autoload the class.
Note New to Laravel? Check out the Laravel Bootcamp for a hands-on tour of the framework while we walk you through building your first Laravel application.
The Root Directory
The App Directory
The app
directory contains the core code of your application. We’ll explore
this directory in more detail soon; however, almost all of the classes in your
application will be in this directory.
The Bootstrap Directory
The bootstrap
directory contains the app.php
file which bootstraps the
framework. This directory also houses a cache
directory which contains
framework generated files for performance optimization such as the route and
services cache files. You should not typically need to modify any files within
this directory.
The Config Directory
The config
directory, as the name implies, contains all of your application’s
configuration files. It’s a great idea to read through all of these files and
familiarize yourself with all of the options available to you.
The Database Directory
The database
directory contains your database migrations, model factories, and
seeds. If you wish, you may also use this directory to hold an SQLite database.
The Public Directory
The public
directory contains the index.php
file, which is the entry point
for all requests entering your application and configures autoloading. This
directory also houses your assets such as images, JavaScript, and CSS.
The Resources Directory
The resources
directory contains your views as well
as your raw, un-compiled assets such as CSS or JavaScript.
The Routes Directory
The routes
directory contains all of the route definitions for your
application. By default, several route files are included with Laravel:
web.php
, api.php
, console.php
, and channels.php
.
The web.php
file contains routes that the RouteServiceProvider
places in the
web
middleware group, which provides session state, CSRF protection, and
cookie encryption. If your application does not offer a stateless, RESTful API
then all your routes will most likely be defined in the web.php
file.
The api.php
file contains routes that the RouteServiceProvider
places in the
api
middleware group. These routes are intended to be stateless, so requests
entering the application through these routes are intended to be authenticated
via tokens and will not have access to session
state.
The console.php
file is where you may define all of your closure based console
commands. Each closure is bound to a command instance allowing a simple approach
to interacting with each command’s IO methods. Even though this file does not
define HTTP routes, it defines console based entry points (routes) into your
application.
The channels.php
file is where you may register all of the
event broadcasting channels that your
application supports.
The Storage Directory
The storage
directory contains your logs, compiled Blade templates, file based
sessions, file caches, and other files generated by the framework. This
directory is segregated into app
, framework
, and logs
directories. The
app
directory may be used to store any files generated by your application.
The framework
directory is used to store framework generated files and caches.
Finally, the logs
directory contains your application’s log files.
The storage/app/public
directory may be used to store user-generated files,
such as profile avatars, that should be publicly accessible. You should create a
symbolic link at public/storage
which points to this directory. You may create
the link using the php artisan storage:link
Artisan command.
The Tests Directory
The tests
directory contains your automated tests. Example
PHPUnit unit tests and feature tests are provided out of
the box. Each test class should be suffixed with the word Test
. You may run
your tests using the phpunit
or php vendor/bin/phpunit
commands. Or, if you
would like a more detailed and beautiful representation of your test results,
you may run your tests using the php artisan test
Artisan command.
The Vendor Directory
The vendor
directory contains your Composer
dependencies.
The App Directory
The majority of your application is housed in the app
directory. By default,
this directory is namespaced under App
and is autoloaded by Composer using the
PSR-4 autoloading standard.
The app
directory contains a variety of additional directories such as
Console
, Http
, and Providers
. Think of the Console
and Http
directories as providing an API into the core of your application. The HTTP
protocol and CLI are both mechanisms to interact with your application, but do
not actually contain application logic. In other words, they are two ways of
issuing commands to your application. The Console
directory contains all of
your Artisan commands, while the Http
directory contains your controllers,
middleware, and requests.
A variety of other directories will be generated inside the app
directory as
you use the make
Artisan commands to generate classes. So, for example, the
app/Jobs
directory will not exist until you execute the make:job
Artisan
command to generate a job class.
Note
Many of the classes in theapp
directory can be generated by Artisan via commands. To review the available commands, run thephp artisan list make
command in your terminal.
The Broadcasting Directory
The Broadcasting
directory contains all of the broadcast channel classes for
your application. These classes are generated using the make:channel
command.
This directory does not exist by default, but will be created for you when you
create your first channel. To learn more about channels, check out the
documentation on event broadcasting.
The Console Directory
The Console
directory contains all of the custom Artisan commands for your
application. These commands may be generated using the make:command
command.
This directory also houses your console kernel, which is where your custom
Artisan commands are registered and your
scheduled tasks are defined.
The Events Directory
This directory does not exist by default, but will be created for you by the
event:generate
and make:event
Artisan commands. The Events
directory
houses event classes. Events may be used to alert
other parts of your application that a given action has occurred, providing a
great deal of flexibility and decoupling.
The Exceptions Directory
The Exceptions
directory contains your application’s exception handler and is
also a good place to place any exceptions thrown by your application. If you
would like to customize how your exceptions are logged or rendered, you should
modify the Handler
class in this directory.
The Http Directory
The Http
directory contains your controllers, middleware, and form requests.
Almost all of the logic to handle requests entering your application will be
placed in this directory.
The Jobs Directory
This directory does not exist by default, but will be created for you if you
execute the make:job
Artisan command. The Jobs
directory houses the
queueable jobs for your application. Jobs may be
queued by your application or run synchronously within the current request
lifecycle. Jobs that run synchronously during the current request are sometimes
referred to as “commands” since they are an implementation of the
command pattern.
The Listeners Directory
This directory does not exist by default, but will be created for you if you
execute the event:generate
or make:listener
Artisan commands. The
Listeners
directory contains the classes that handle your
events. Event listeners receive an event instance
and perform logic in response to the event being fired. For example, a
UserRegistered
event might be handled by a SendWelcomeEmail
listener.
The Mail Directory
This directory does not exist by default, but will be created for you if you
execute the make:mail
Artisan command. The Mail
directory contains all of
your classes that represent emails sent by your
application. Mail objects allow you to encapsulate all of the logic of building
an email in a single, simple class that may be sent using the Mail::send
method.
The Models Directory
The Models
directory contains all of your
Eloquent model classes. The Eloquent ORM included
with Laravel provides a beautiful, simple ActiveRecord implementation for
working with your database. Each database table has a corresponding “Model”
which is used to interact with that table. Models allow you to query for data in
your tables, as well as insert new records into the table.
The Notifications Directory
This directory does not exist by default, but will be created for you if you
execute the make:notification
Artisan command. The Notifications
directory
contains all of the “transactional”
notifications that are sent by your
application, such as simple notifications about events that happen within your
application. Laravel’s notification feature abstracts sending notifications over
a variety of drivers such as email, Slack, SMS, or stored in a database.
The Policies Directory
This directory does not exist by default, but will be created for you if you
execute the make:policy
Artisan command. The Policies
directory contains the
authorization policy classes for your
application. Policies are used to determine if a user can perform a given action
against a resource.
The Providers Directory
The Providers
directory contains all of the
service providers for your application. Service
providers bootstrap your application by binding services in the service
container, registering events, or performing any other tasks to prepare your
application for incoming requests.
In a fresh Laravel application, this directory will already contain several providers. You are free to add your own providers to this directory as needed.
The Rules Directory
This directory does not exist by default, but will be created for you if you
execute the make:rule
Artisan command. The Rules
directory contains the
custom validation rule objects for your application. Rules are used to
encapsulate complicated validation logic in a simple object. For more
information, check out the
validation documentation.
API
Current
api?request=players
-
ordered by rank
- should be optional or under
Leaderboards
- when is rank
null
?
- should be optional or under
- elo_peak
- last_series_time
- team subobject
- link to player page
- link to team page
- link to player api
{
"id": 29,
"name": "TheViper",
"elo": 2419,
"elo_peak": 2419,
"rank": 2,
"url": "\/player\/29\/TheViper",
"api_url": "https:\/\/aoe-elo.com\/api?request=player&id=29",
"team_name": "GamerLegion",
"last_series_time": "2023-08-27"
}
api?request=player&id=1
{
"id": 1,
"name": "DauT",
"elo": 2336,
"rank": 6,
"url": "https:\/\/aoe-elo.com\/player\/1\/DauT",
"steam_id": null,
"first_series_timestamp": 1030751999,
"first_series_time": "Aug 2002",
"peak_timestamp": 1693094400,
"peak_time": "Aug 2023",
"peak_elo": 2336,
"inactive": false,
"retired": false,
"series_played": 557,
"series_won": 374,
"games_played": 1882,
"tournaments_played": 149,
"tournaments_list": [
2,
4,
6,
7,
...
]
}
/api?request=tournaments
{
"id": 599,
"name": "MetaWorldTour",
"url": "https:\/\/aoe-elo.com\/tournament\/599\/MetaWorldTour",
"api_url": "https:\/\/aoe-elo.com\/api?request=tournament&id=599",
"start_timestamp": 1693267200,
"end_timestamp": 1698019199
}
api?request=tournament&id=40
{
"id": 40,
"name": "Escape Gaming Masters 3",
"type": "cup",
"start_timestamp": 1509494400,
"end_timestamp": 1510617599,
"players": [
1,
11,
14,
16,
24,
29,
30,
31,
55,
94,
100,
103
],
"url": "https:\/\/aoe-elo.com\/tournament\/40\/Escape-Gaming-Masters-3",
"series": 18
}
Next
Possible additional stats
endpoints
/api/v1/players/{id}/stats
/api/v1/teams/{id}/stats
/api/v1/tournaments/{id}/stats
/api/v1/sets/{id}/stats
/api/v1/leaderboards/{id}/stats
/api/v1/search
-
search for players, teams, tournaments, sets, leaderboards
- search for players by steam_id
- search for players by relic_link_id
- search for players by name
- search for teams by name
- search for tournaments by name
-
create index with model name, e.g.
App\Models\Player
andApp\Models\Team
- so they can be easily access afterwards by casting them to a variable
/api/v1/players
/api/v1/players/
/api/v1/teams
/api/v1/teams/
/api/v1/tournaments
/api/v1/tournaments/
/api/v1/sets
/api/v1/sets/
/api/v1/leaderboards
/api/v1/leaderboards/
External APIs
Liquipedia
Querying for sets, tournaments, player data, etc.
-
documentation: https://api.liquipedia.net/documentation/api/v3
-
special endpoints: https://liquipedia.net/ageofempires/Special:LiquipediaDB/Arabia
-
matches need to be queried for
ageofempires
onv1
endpoint as they are not migrated tov3
yet- so
v1/match
instead ofv3/match2
- so
esportsearnings
- documentation: https://www.esportsearnings.com/apidocs
aoe2map
Querying for map names
- endpoint: https://aoe2map.net/api/allmaps
aoc-ref-data
Querying for validated player data, teams might be outdated and not kept up-to-date
- players: https://raw.githubusercontent.com/SiegeEngineers/aoc-reference-data/master/data/players.yaml’
- teams: https://raw.githubusercontent.com/SiegeEngineers/aoc-reference-data/master/data/teams.json
Database
Migrating from the old schema
Use atlas
to extract the schemas:
atlas schema inspect -u "sqlite://database_old.sqlite" --format '{{ json . }}' > schema_old.json
atlas schema inspect -u "sqlite://database.sqlite" --format '{{ json . }}' > schema.json
You can use jq and the playground for looking up values. This is the root path for the database tables:
.[][].tables[]
To filter out the legacy_
and the migration
tables and sort for the name of
the table:
jq --sort-keys '[.[][].tables[] | select(.name | contains("legacy_") or contains("migration") | not)] | sort_by(.name)'
New schema
The new schema is based on the old one, but with some changes:
Migration Guide
1. Analysis
- Understand the Legacy System: Begin by fully understanding the legacy database schema. Map out tables, relationships, indexes, views, stored procedures, and any other objects.
- Determine Requirements: Document the requirements for the new schema. These might be based on performance needs, newer features, or simply a more logical structuring.
- Identify Data Types and Transformations: Some data types in the legacy database might not directly map to the new system. Identify these mismatches early.
2. Design
- Schema Design: Design the new schema keeping in mind best practices for the specific database platform, as well as any new requirements.
- Mapping Document: Create a detailed mapping document to show how data will move from the legacy system to the new one. This should include any transformations or translations required.
3. Development
- Write Migration Scripts: Based on the mapping document, write scripts or use ETL (Extract, Transform, Load) tools to transfer data from the old to the new schema.
- Handle Special Cases: Some data might not transfer cleanly, or might need special processing. Write scripts or processes to handle these cases.
- Test the Scripts: Before running the migration in a production environment, test the scripts on a backup or a copy of the legacy database.
4. Testing
- Staging Environment: Set up a staging environment that mimics the production system. Run the migration scripts here first.
- Data Validation: After migration, validate the data in the new schema. Ensure that all data was transferred correctly and that there are no missing or corrupted records.
- Performance Testing: Check the performance of the new schema under load to ensure it meets the requirements.
5. Migration
- Backup: Always backup the legacy system before starting the migration.
- Downtime: Depending on the migration approach, you might need some downtime. Inform stakeholders and users in advance.
- Execute Migration: Run the migration scripts on the production database.
- Monitor: After migration, monitor the database for any errors, performance issues, or other potential problems.
6. Post-Migration
- Optimization: Based on the monitoring, make any required optimizations to the new schema or system.
- Documentation: Update all system documentation to reflect the new database schema and any changes in processes.
- Inform Stakeholders: Once the migration is complete and stable, inform all relevant stakeholders.
Database Mapping
Country / countries
Table | Column | Data Type | Nullable | Indexes | Primary Key | Maps to |
---|---|---|---|---|---|---|
country | id | int unsigned | false | true | countries.id | |
country | iso_key | varchar(10) | false | false | countries.iso_key | |
country | name | text | false | false | countries.name |
Match 1v1 / sets + set_items
Table | Column | Data Type | Nullable | Indexes | Primary Key | Comment | Maps to |
---|---|---|---|---|---|---|---|
match_1v1 | id | int unsigned | false | true | sets.id | ||
match_1v1 | date | date | true | false | when this match was played (null=auto from tournament) | sets.played_at (conversion to dateTime) | |
match_1v1 | tournament_id | int unsigned | true | false | sets.tournament_id (foreign) | ||
match_1v1 | stage_id | int unsigned | false | false | sets.stage_id (foreign) | ||
match_1v1 | player_1_id | int unsigned | false | false | set_item.entity_id (foreign) | ||
match_1v1 | player_2_id | int unsigned | false | false | set_item.entity_id (foreign) | ||
match_1v1 | score_1 | int unsigned | false | false | set_item.score | ||
match_1v1 | score_2 | int unsigned | false | false | set_item.score | ||
match_1v1 | create_user | int unsigned | false | created_user | false | sets.created_user_id (foreign) | |
match_1v1 | create_time | datetime | false | false | timestamps() | ||
match_1v1 | update_user | int unsigned | false | update_user | false | sets.updated_user_id (foreign) | |
match_1v1 | update_time | datetime | false | false | timestamps() |
Match 1v1 Event / - (removed)
Table | Column | Data Type | Nullable | Indexes | Primary Key | Maps to |
---|---|---|---|---|---|---|
match_1v1_event | id | int unsigned | false | true | - | |
match_1v1_event | date | date | true | false | - | |
match_1v1_event | time | time | true | false | - | |
match_1v1_event | tournament_id | int unsigned | false | tournament_id_2 | false | - |
match_1v1_event | stage_id | int unsigned | false | stage_id | false | - |
match_1v1_event | player_1_id | int unsigned | true | tournament_id | false | - |
match_1v1_event | player_2_id | int unsigned | true | tournament_id | false | - |
match_1v1_event | bo | int unsigned | true | false | - | |
match_1v1_event | create_user | int unsigned | false | create_user | false | - |
match_1v1_event | create_time | datetime | false | false | - | |
match_1v1_event | update_user | int unsigned | false | update_user | false | - |
match_1v1_event | update_time | datetime | false | false | - |
Player / players
Table | Column | Data Type | Nullable | Indexes | Primary Key | Comment | Maps to |
---|---|---|---|---|---|---|---|
player | id | int unsigned | false | true | players.id | ||
player | name | varchar(30) | false | name | false | players.name | |
player | alias | varchar(255) | false | false | comma-separated | players.aliases (JSON array of alias names: [“alias1”, “alias2”]) | |
player | team_id | int unsigned | true | team_id | false | players.team_id (foreign) | |
player | country_key | varchar(10) | true | false | players.country_id (foreign, lookup id?) | ||
player | initial_elo_1v1 | int unsigned | true | false | players.base_elo | ||
player | voobly_id | int unsigned | true | false | players.voobly_id | ||
player | steam_id | varchar(40) | true | false | players.steam_id | ||
player | steam_id_failed | varchar(40) | true | false | players.steam_id_failed | ||
player | twitch | text | true | false | merge into players.socials (‘{“name”: “twitch”, “value”: “ | ||
player | youtube | text | true | false | merge into players.socials (‘{“name”: “youtube”, “value”: “ | ||
player | text | true | false | merge into players.socials (‘{“name”: “twitter”, “value”: “ | |||
player | text | true | false | merge into players.socials (‘{“name”: “facebook”, “value”: “ | |||
player | create_user | int unsigned | false | create_user | false | players.created_user_id (foreign) | |
player | create_time | datetime | false | false | timestamps() | ||
player | update_user | int unsigned | false | update_user | false | players.updated_user_id (foreign) | |
player | update_time | datetime | false | false | timestamps() |
Player Info / players_info (TODO: what is the use case for this?)
Table | Column | Data Type | Nullable | Indexes | Primary Key | Maps to |
---|---|---|---|---|---|---|
player_info | id | int unsigned | false | true | players_info.id | |
player_info | player | int unsigned | false | player | false | players_info.player_id (foreign) |
player_info | type | varchar(255) | false | false | players_info.type | |
player_info | value_int | int | true | false | players_info.value_int | |
player_info | value_str | text | true | false | players_info.value_str | |
player_info | create_time | datetime | false | false | timestamps() | |
player_info | create_user | int unsigned | false | create_user | false | players_info.created_user_id (foreign) |
Stage / stages
Table | Column | Data Type | Nullable | Indexes | Primary Key | Maps to |
---|---|---|---|---|---|---|
stage | id | int unsigned | false | true | stages.id | |
stage | name | text | false | false | stages.name | |
stage | bracket | int unsigned | false | false | stages.bracket (TODO: could be foreign and own table?) | |
stage | index | int unsigned | false | false | stages.index | |
stage | weight | float | false | false | stages.weight (convert to integer 1 => 10, intval(stage.weight * 10)) | |
stage | importance | int unsigned | false | false | stages.importance |
Team / teams
Table | Column | Data Type | Nullable | Indexes | Primary Key | Maps to |
---|---|---|---|---|---|---|
team | id | int unsigned | false | true | teams.id | |
team | name | varchar(100) | false | name | false | teams.name |
team | tag | varchar(30) | false | false | teams.tag | |
team | primary_color | varchar(30) | true | false | teams.primary_color | |
team | secondary_color | varchar(30) | true | false | teams.secondary_color | |
team | create_user | int unsigned | true | create_user | false | teams.created_user_id (foreign) |
team | create_time | datetime | true | false | timestamps() | |
team | update_user | int unsigned | true | update_user | false | teams.updated_user_id (foreign) |
team | update_time | datetime | true | false | timestamps() |
Tournament / tournaments
Table | Column | Data Type | Nullable | Indexes | Primary Key | Comment | Maps to |
---|---|---|---|---|---|---|---|
tournament | id | int unsigned | false | true | tournaments.id | ||
tournament | name | varchar(255) | false | name | false | tournaments.name | |
tournament | short | varchar(100) | false | false | tournaments.short_name | ||
tournament | start | date | true | false | tournaments.started_at (conversion to dateTime) | ||
tournament | end | date | true | false | tournaments.ended_at (conversion to dateTime) | ||
tournament | weight | int unsigned | false | false | tournaments.weight | ||
tournament | type | enum(‘cup’,‘qualifier’) | false | false | tournaments.type | ||
tournament | prizemoney | int unsigned | true | false | in $ | tournaments.prize_money | |
tournament | parent_id | int unsigned | true | parent_id | false | tournaments.parent_tournament_id (foreign) | |
tournament | structure | enum(‘single-elemination’,‘double-elimination’,‘league’,‘other’,‘group’,‘group-ko’) | false | false | tournaments.structure | ||
tournament | evaluation | varchar(30) | true | false | tournaments.evaluation | ||
tournament | website | text | true | false | tournaments.website | ||
tournament | comment | text | true | false | tournaments.comments | ||
tournament | create_user | int unsigned | false | create_user | false | tournaments.created_user_id (foreign) | |
tournament | create_time | datetime | false | false | timestamps() | ||
tournament | update_user | int unsigned | false | update_user | false | tournaments.updated_user_id (foreign) | |
tournament | update_time | datetime | false | false | timestamps() |
Tournament Info / tournaments_info
Table | Column | Data Type | Nullable | Indexes | Primary Key | Comment | Maps to |
---|---|---|---|---|---|---|---|
tournament_info | id | int unsigned | false | true | tournaments_info.id | ||
tournament_info | create_user | int unsigned | false | create_user | false | tournaments_info.created_user_id (foreign) | |
tournament_info | create_time | datetime | false | false | timestamps() | ||
tournament_info | tournament_id | int unsigned | false | tournament_id | false | tournaments_info.tournament_id (foreign) | |
tournament_info | type | int unsigned | false | type | false | 1: challonge bracket, 2: bracket URL, 3: public res., 4: private res. | tournaments_info.type (enum) |
tournament_info | description | text | false | false | tournaments_info.description | ||
tournament_info | value | text | false | false | tournaments_info.value |
Tournament Result / tournaments_results
Table | Column | Data Type | Nullable | Indexes | Primary Key | Comment | Maps to |
---|---|---|---|---|---|---|---|
tournament_result | id | int unsigned | false | true | tournaments_results.id | ||
tournament_result | tournament | int unsigned | false | tournament | false | tournaments_results.tournament_id (foreign) | |
tournament_result | player | int unsigned | false | player | false | tournaments_results.player_id (foreign) | |
tournament_result | type | int unsigned | true | type | false | 1: win, …, 5: semi-finals, null: other | tournaments_results.type |
tournament_result | money | int unsigned | true | false | tournaments_results.money | ||
tournament_result | source | text | true | false | tournaments_results.source | ||
tournament_result | create_time | datetime | false | false | timestamps() | ||
tournament_result | create_user | int unsigned | false | create_user | false | tournaments_results.created_user_id (foreign) |
User / users .. permissions
Table | Column | Data Type | Nullable | Indexes | Primary Key | Comment | Maps to |
---|---|---|---|---|---|---|---|
user | id | int unsigned | false | true | users.id | ||
user | name | varchar(100) | false | name | false | users.name | |
user | pass | varchar(255) | false | false | removed due to social logins | ||
user | rank | int unsigned | false | false | 1: admin, 2: normal | migrate to permission system | |
user | allow_tournament | int unsigned | true | false | 0: nothing, 1: create, 2: and update, 3: and remove | migrate to permission system | |
user | allow_player | int unsigned | true | false | migrate to permission system | ||
user | allow_match | int | true | false | migrate to permission system | ||
user | allow_see | int unsigned | true | false | 0: nothing, 1: stats | migrate to permission system |
Implementation
General
- Routing: https://laravel.com/docs/10.x/routing
- Controller: https://laravel.com/docs/10.x/controllers
- Authentication: https://laravel.com/docs/10.x/sanctum
- ORM: https://laravel.com/docs/10.x/eloquent
Best practices
Performance
- https://kinsta.com/blog/laravel-performance/
- https://newrelic.com/blog/best-practices/improve-laravel-performance
- https://www.cloudways.com/blog/laravel-performance-optimization/
- https://serversforhackers.com/laravel-perf/eager-loading
Next steps
- implement Elo calculation
-
implement
review
system andada
funtionality -
create
drafts
table and implementdraft
functionality -
implement API Endpoints for
Players
,Teams
,Sets
,Tournaments
,Search
, andLeaderboads
- check https://github.com/nette/utils for functionality
Elo
- implement Elo calculation algorithm
- implement Elo caching mechanism
Authentication
- Discord: https://socialiteproviders.com/Discord/
- Config Setup
- Acquire Client ID and Secret
- Add Redirect URL
-
return Socialite::driver('discord')->redirect();
- Steam: https://socialiteproviders.com/Steam/
- Config Setup
- Acquire Client ID and Secret
- Add Redirect URL
-
return Socialite::driver('discord')->redirect();
- Twitch: https://socialiteproviders.com/Twitch/
- Config Setup
- Acquire Client ID and Secret
- Add Redirect URL
-
return Socialite::driver('discord')->redirect();
- Github: https://socialiteproviders.com/Github/ (Socialite Base)
- Config Setup
- Acquire Client ID and Secret
- Add Redirect URL
-
return Socialite::driver('discord')->redirect();
- possible others
- Google: https://socialiteproviders.com/Google/ (Socialite Base)
Permissions
Architecture
- create overview/bird’s eye
Database
- Check ER diagram
- Create ‘news’ table + model + controller
- Check models
- Create migration script from old schema to new one
- Optimize queries: https://omarbarbosa.com/posts/optimization-of-eloquent-queries-to-reduce-memory-usage
- create legacy->new database mapping
- finish design of new database schema
-
generate new models from database
- check if the relations are correct
- implement repository pattern via interfaces
-
migrate data from legacy to the new database schema
-
use
command
andseeder
setup
-
use
Models
- implement metadata for ArdPlayer
-
implement metadata for player/team import
-
check how to get all metadata for an
ArdPlayer
-
check how to get all metadata for an
-
move
Player::aliases
toPlayer::metadata->alias
-
move
Player::other_socials
toPlayer::metadata->socials