> *سلام بر کدکاپ!*
>
> — کدکاپیها
بچههای کدکاپ قصد دارند تا قبل از شروع مسابقه اصلی، مسابقهای را ترتیب دهند و سؤالات را تست کنند، اما آنها نمیخواهند تا تغییرات در دیتابیس اصلی پروژه اعمال شوند آنها. از شما میخواهند تا لاراول را طوری کانفیگ کنید که بتوانند بهراحتی با دیتابیسهایشان کار کنند.
# جزئیات پروژه
پروژهی اولیه را از [این لینک](/contest/assignments/35048/download_problem_initial_project/124281/) دانلود کنید.
<details class="grey">
<summary>ساختار فایلها</summary>
```
codecup_test_connection
├── app
├── bootstrap
├── config
│ ├── app.php
│ ├── auth.php
│ ├── broadcasting.php
│ ├── cache.php
│ ├── cors.php
│ ├── database.php
│ ├── filesystems.php
│ ├── hashing.php
│ ├── logging.php
│ ├── mail.php
│ ├── queue.php
│ ├── sanctum.php
│ ├── services.php
│ ├── session.php
│ └── view.php
├── database
│ ├── factories
│ ├── migrations
│ ├── seeders
│ ├── codecup.sqlite
│ └── codecup_test.sqlite
├── public
├── resources
├── routes
│ ├── api.php
│ ├── channels.php
│ ├── console.php
│ └── web.php
├── storage
├── tests
├── README.md
├── artisan
├── composer.json
├── composer.lock
├── package.json
├── phpunit.xml
├── server.php
└── webpack.mix.js
```
</details>
<details class="brown">
<summary>راهاندازی پروژه</summary>
**برای اجرای پروژه، باید `php` و `composer` را از قبل نصب کرده باشید.**
+ ابتدا پروژهی اولیه را دانلود و از حالت فشرده خارج کنید.
+ دستور `composer install` را در پوشهی اصلی پروژه برای نصب نیازمندیها اجرا کنید.
+ برای اجرای تستهای نمونه، میتوانید از دستور `php artisan test` استفاده کنید.
</details>
در دایرکتوری `database` دو دیتابیس `codecup.sqlite` و `codecup_test.sqlite` وجود دارد. درون هر یک از این دیتابیسها جدولی با همان نام دیتابیس وجود دارد و اطلاعاتی نیز داخل جدولها وجود دارد.
شما باید تنظیماتی را فراهم کنید تا بتوان اتصالی با هر یک از این دیتابیسها برقرار کرد و محتوای جداول دلخواه را برگرداند. برای مثال، کد زیر باید اطلاعات جدول `codecup_test` داخل دیتابیس `codecup_test` را برگرداند:
```php
DB::connection('codecup_test')->table('codecup_test')->get();
```
نام کانکشن دیتابیس `codecup.sqlite` باید برابر با `codecup` باشد و نام کانکشن دیتابیس `codecup_test.sqlite` باید برابر با `codecup_test` باشد.
# نکات
+ محتوای اطلاعاتی که از جداول برگردانده میشوند اهمیتی ندارد و فقط ملاک داوری برقراری ارتباط با دیتابیسها و اجرای کوئری روی جداول آنها است.
+ شما تنها مجاز به اعمال تغییرات در فایل `config/database.php` هستید.
# آنچه باید آپلود کنید
پس از اعمال تغییرات، فایل `config/database.php` را آپلود کنید.
> *امان از ویرایشهای ویکیپدیا!*
>
> — حامد
حامد که از محتوای ویکیپدیا بسیار ناراحت و خسته شده، قصد دارد تا آن را به شیوهای که خودش دوست دارد پیادهسازی کند. او میخواهد رفتار تمام کاربرانی که پستی را ویرایش میکنند را زیر نظر داشته باشد و همچنین اطلاعات سازنده و آخرین ویرایشکننده را هم سریعاً بهدست بیاورد.
شما باید برای رسیدن به این هدف به او کمک کنید.
# جزئیات پروژه
پروژهی اولیه را از [این لینک](/contest/assignments/35048/download_problem_initial_project/124282/) دانلود کنید.
<details class="grey">
<summary>ساختار فایلها</summary>
```
qpedia
├── app
│ ├── Console
│ ├── Exceptions
│ ├── Http
│ ├── Models
│ │ ├── Edit.php
│ │ ├── Post.php
│ │ └── User.php
│ └── Providers
├── bootstrap
├── config
├── database
│ ├── factories
│ │ └── UserFactory.php
│ ├── migrations
│ │ ├── 2014_10_12_000000_create_users_table.php
│ │ ├── 2014_10_12_100000_create_password_resets_table.php
│ │ ├── 2019_08_19_000000_create_failed_jobs_table.php
│ │ ├── 2019_12_14_000001_create_personal_access_tokens_table.php
│ │ ├── 2021_11_30_135802_create_posts_table.php
│ │ ├── 2021_11_30_135820_create_edits_table.php
│ │ └── 2021_11_30_140323_create_edit_post_table.php
│ └── seeders
├── public
├── resources
├── routes
├── storage
├── tests
├── README.md
├── artisan
├── composer.json
├── composer.lock
├── package.json
├── phpunit.xml
├── server.php
└── webpack.mix.js
```
</details>
<details class="brown">
<summary>راهاندازی پروژه</summary>
**برای اجرای پروژه، باید `php` و `composer` را از قبل نصب کرده باشید.**
+ ابتدا پروژهی اولیه را دانلود و از حالت فشرده خارج کنید.
+ دستور `composer install` را در پوشهی اصلی پروژه برای نصب نیازمندیها اجرا کنید.
+ برای اجرای تستهای نمونه، میتوانید از دستور `php artisan test` استفاده کنید.
</details>
## مایگریشنها
مایگریشنهای مربوط به پروژه از قبل ایجاد شدهاند. در اولین گام باید ساختار جداول پروژه را مطابق توضیحات زیر تکمیل کنید:
مایگریشن `posts`: ستونهای زیر را به مایگریشن جدول `posts` اضافه کنید:
|نام ستون|نوع|تعریف|
|:-------|:--|----:|
|`user_id`|`bigInteger`|کلید خارجی به جدول `users` و ستون `id`|
|`title`|`string`|عنوان|
|`body`|`text`|بدنه|
|`created_by`|`bigInteger`|آیدی کاربر ایجادکنندهی پست|
|`updated_by`|`bigInteger`|آیدی آخرین کاربر بهروزکنندهی پست|
مایگریشن `edits`: ستونهای زیر را به مایگریشن جدول `edits` اضافه کنید:
|نام ستون|نوع|تعریف|
|:-------|:--|----:|
|`user_id`|`bigInteger`|کلید خارجی به جدول `users` و ستون `id`|
|`change`|`string`|ستون تغییریافته مثلا `title`|
|`value`|`string`|مقدار جدید|
سپس باید بین این جداول روابط زیر را برقرار کنید:
+ هر کاربر میتواند چندین پست ایجاد کند و هر پست متعلق به یک کاربر است.
+ هر کاربر میتواند چندین ویرایش بر روی پستها داشته باشد و هر ویرایش را یک کاربر ایجاد کرده است.
+ بین جدول پستها و جدول ویرایشها باید رابطهی چندبهچند وجود داشته باشد. مایگریشن مربوط به جدول میانی این رابطه هم که از قبل وجود دارد نیز باید تکمیل شود.
ساختار نهایی جداول باید بهصورت زیر باشد:
![نمودار ER](https://quera.ir/qbox/view/HoiySPgVH6/er.png)
## مدلها
در آخرین گام شما باید امکانی را به این پروژه اضافه کنید که اگر پستی ایجاد شد، مقادیر `created_by` و `updated_by` این پست با آیدی کاربری که این پست را ایجاد کرده پر شود و اگر پستی آپدیت شد، مقدار `updated_by` آن نیز با آیدی کاربری که این پست را آپدیت کرد پر شود. در این حالت که پست آپدیت شد، باید مقدار تغییریافتهی پست را به دست آورده و آن را به جدول ویرایشها اضافه کنید.
برای درک بهتر سؤال، فرض کنید کاربری با آیدی `1` پستی را با عنوان `تست` و محتوا `تست تست تست` ایجاد کرده. مقادیر `created_by` و `updated_by` این پست باید با مقدار `1` در دیتابیس وجود داشته باشند. حال فرض کنید کاربری با آیدی `2` عنوان این پست را به `test` ویرایش میکند. در این حالت باید مقدار `updated_by` این پست به آیدی `2` تغییر کند و همچنین یک سطر به صورت زیر در جدول ویرایشها اضافه شود:
|user_id|change|value|
|:--|:----|:----|
|2|title|test|
# نکات
+ **تضمین میشود** که هنگام ویرایش پستها کاربر در برنامه لاگین کرده است.
+ شما تنها مجاز به اعمال تغییرات در پوشههای `app/Models` و `database/migrations` هستید.
+ شما باید برای هر تغییر، یک سطر به جدول ویرایشها اضافه کنید؛ یعنی اگر در مثال قبل علاوه بر عنوان پست، محتوای پست هم تغییر میکرد، باید یک سطر دیگر هم به جدول ویرایشها اضافه میشد.
# آنچه باید آپلود کنید
پس از پیادهسازی موارد خواستهشده، پوشههای `app` و `database` را زیپ کرده و آپلود کنید.
اخیراً به هوتن تسکی داده شده که باید دیتابیسی پر از جدول با روابط متعدد ایجاد کند. او که از این کار خسته شده، ایدهای به ذهنش رسیده که این فرایند را خودکار کند. برای این کار به هوتن کمک کنید.
# جزئیات پروژه
پروژهی اولیه را از [این لینک](/contest/assignments/35048/download_problem_initial_project/124279/) دانلود کنید.
<details class="grey">
<summary>ساختار فایلها</summary>
```
model-maker
├── app
├── bootstrap
├── config
├── database
├── public
├── resources
├── routes
├── storage
├── tests
├── README.md
├── artisan
├── composer.json
├── composer.lock
├── package.json
├── phpunit.xml
├── server.php
└── webpack.mix.js
```
</details>
<details class="brown">
<summary>راهاندازی پروژه</summary>
**برای اجرای پروژه، باید `php` و `composer` را از قبل نصب کرده باشید.**
+ ابتدا پروژهی اولیه را دانلود و از حالت فشرده خارج کنید.
+ دستور `composer install` را در پوشهی اصلی پروژه برای نصب نیازمندیها اجرا کنید.
+ برای اجرای تستهای نمونه، میتوانید از دستور `php artisan test` استفاده کنید.
</details>
شما باید دستوری را ایجاد کنید تا مدلها و مایگریشنهایی را با جزئیاتی که در ادامه گفته میشود، ایجاد کند.
قالب دستوری که میسازید باید بهصورت زیر باشد:
```shell
php artisan make:custom-model <mark title="نام مدل اول">MODEL_NAME_1</mark> <mark title="نام مدل دوم">MODEL_NAME_2</mark> --type=<mark title="نوع رابطه">RELATION_TYPE</mark>
```
دستور فوق به این معنی است که باید دو مدل با نامهای `MODEL_NAME_1` و `MODEL_NAME_2` ایجاد شوند و بین این دو مدل رابطهای از نوع `RELATION_TYPE` برقرار شود. مقدار `RELATION_TYPE` میتواند یکی از مقادیر `11`، `1n` یا `nn` باشد. `11` بهمعنی رابطهی *One to One* ، `1n` بهمعنی رابطهی *One to Many* و `nn` بهمعنی رابطهی *Many to Many* است.
## مثال
فرض کنید دستور زیر اجرا میشود:
```shell
php artisan make:custom-model <mark title="نام مدل اول">Author</mark> <mark title="نام مدل دوم">Book</mark> --type=<mark title="نوع رابطه">nn</mark>
```
با اجرای دستور فوق، باید فایلهای `Author.php` و `Book.php` در دایرکتوری `app/Models` ایجاد شوند. محتویات فایلهای این دو کلاس باید بهصورت زیر باشد:
<details class="red">
<summary>محتویات مدلها</summary>
```php app/Models/Author.php laravel
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Author extends Model
{
use HasFactory;
public function books()
{
return $this->belongsToMany(Book::class);
}
}
```
```php app/Models/Book.php laravel
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
use HasFactory;
public function authors()
{
return $this->belongsToMany(Author::class);
}
}
```
</details>
همچنین مایگرشنهای مورنیاز مدلها و رابطهشان باید ایجاد شود:
<details class="red">
<summary>محتویات مایگریشنها</summary>
```php database/migrations/2021_11_16_202919_create_authors_table.php laravel
class CreateAuthorsTable extends Migration
{
public function up()
{
Schema::create('authors', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('authors');
}
}
```
```php database/migrations/2021_11_16_202921_create_books_table.php laravel
class CreateBooksTable extends Migration
{
public function up()
{
Schema::create('books', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('books');
}
}
```
```php database/migrations/2021_11_16_202922_create_author_book_table.php laravel
class CreateAuthorBookTable extends Migration
{
public function up()
{
Schema::create('author_book', function (Blueprint $table) {
$table->id();
$table->foreignId('author_id')->constrained()->onDelete('cascade');
$table->foreignId('book_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('author_book');
}
}
```
</details>
نکتهی مهمی که باید به آن دقت کنید نام روابط در مدلهاست، که باید بهصورت زیر قرار دهید:
+ اگر نوع رابطه مفرد به مفرد بود نام رابطه باید بهصورت مفرد کلاس هدف قرار گیرد مثلا فرض کنید بین دو مدل `User` و `Name` رابطه یکبهیک برقرار است پس نام رابطه در مدل `User` باید برابر `name` قرار گیرد.
+ اگر نوع رابطه مفرد به جمع بود نام رابطه باید بهصورت جمع کلاس هدف قرار گیرد مثل مدلهای `Author` و `Book` مثال ذکر شده در همین سوال.
**تضمین میشود** که در دستور نام مدلها بهصورت صحیح داده میشوند.
# نکات
+ برای هر رابطه باید مدل و مایگریشن متناسب با **نوع رابطه** ایجاد شود.
+ در نامگذاری جداول، باید از ***convention* لاراول** پیروی کنید.
+ تمامی این فرایندها باید بهصورت خودکار انجام شود و در داوری، ساخت مدلهای مختلف تست میشود.
+ برای ساخت مایگریشنها حتماً به زمان ساخت مایگریشنها دقت کنید تا بهترتیب اجرا شوند.
+ شما تنها مجاز به اعمال تغییرات در پوشه `app` و در صورت نیاز `stubs` هستید.
# آنچه باید آپلود کنید
پس از پیادهسازی موارد خواستهشده، پوشهی `app` و پوشهی `stubs` را (در صورت وجود) زیپ کرده و آپلود کنید.
مهدی برنامهنویس خفنی است و پروژههای خفنی انجام میدهد. دوستان او از نحوهی پیادهسازی سیستم داوری کوئرا در عجباند، اما مهدی به آنها میگوید *پیادهسازی جاجی مثل کوئرا عین آب خوردنه*! مهدی میخواهد این موضوع را به دوستانش اثبات کند، اما در حال حاضر مشغول انجام پروژههای خفن است و فرصت انجام این کار را ندارد.
از شما میخواهیم این موضوع را به دوستان مهدی اثبات کنید.
# جزئیات پروژه
پروژهی اولیه را از [این لینک](/contest/assignments/35048/download_problem_initial_project/124280/) دانلود کنید.
<details class="grey">
<summary>ساختار فایلها</summary>
```
judge-api
├── app
│ ├── Console
│ ├── Exceptions
│ ├── Http
│ ├── Jobs
│ │ └── JudgeSubmission.php
│ ├── Judge
│ │ ├── InputOutputJudge.php
│ │ ├── Judge.php
│ │ └── JudgeResult.php
│ ├── Models
│ │ ├── Submission.php
│ │ └── User.php
│ └── Providers
├── bootstrap
├── config
├── database
├── public
├── resources
├── routes
├── storage
├── tests
├── README.md
├── artisan
├── composer.json
├── composer.lock
├── package.json
├── phpunit.xml
├── server.php
└── webpack.mix.js
```
</details>
<details class="brown">
<summary>راهاندازی پروژه</summary>
**برای اجرای پروژه، باید `php` و `composer` را از قبل نصب کرده باشید.**
+ ابتدا پروژهی اولیه را دانلود و از حالت فشرده خارج کنید.
+ دستور `composer install` را در پوشهی اصلی پروژه برای نصب نیازمندیها اجرا کنید.
+ برای اجرای تستهای نمونه، میتوانید از دستور `php artisan test` استفاده کنید.
</details>
همانطور که میدانید، سیستم داوری کوئرا (در حالت ورودی/خروجی) به اینصورت است که یک رشته بهعنوان *stdin* به یک برنامه داده میشود و در انتها، نتیجهی اجرای برنامه دریافت میشود.
## مدلها
علاوه بر مدل پیشفرض `User`، مدلی با نام `Submission` در برنامه تعریف شده است. مایگریشنها و روابط بین مدلها از قبل برقرار شدهاند. با مطالعهی این موارد، باید با ساختار مدلهای برنامه آشنا شوید.
## کلاس `App\Judge\JudgeResult`
از این کلاس برای نگهداری اطلاعات مربوط به داوری ارسالها استفاده میشود.
```php app/Judge/JudgeResult.php laravel
class JudgeResult
{
const SUCCESS = 0;
const TIME_LIMIT_EXCEEDED = 1;
const RUNTIME_ERROR = 2;
const SYNTAX_ERROR = 3;
public function __construct(
public string $stderr,
public string $stdout,
public int $status
) {}
}
```
## اینترفیس `App\Judge\Judge`
این اینترفیس شامل یک متد با نام `run` است که پارامترهای آن بهصورت زیر هستند:
+ `code`: متن کد
+ `timeLimit`: محدودیت زمان برحسب **میلیثانیه**
+ `stdin`: ورودی برنامه
```php app/Judge/Judge.php laravel
interface Judge
{
public function run(string $code, int $timeLimit, string $stdin = ''): JudgeResult;
}
```
## کلاس `App\Judge\InputOutputJudge`
این کلاس یک پیادهسازی از اینترفیس `App\Judge\Judge` است که باید کدهای *PHP* را اجرا کند و نتیجه را در قالب آبجکتی از کلاس `App\Judge\JudgeResult` برگرداند. مقدار پراپرتیهای آبجکت خروجی باید مطابق توضیحات زیر باشد:
+ اگر کد سینتکس ارور داشته باشد، مقدار `status` باید برابر با `JudgeResult::SYNTAX_ERROR` باشد و مقدار `stderr` و `stdout` باید برابر با رشتهی خالی باشد.
+ اگر اجرای برنامه در زمان مشخصشده خاتمه یابد و دادهای توسط برنامه در `stderr` نوشته نشده باشد، مقدار `status` باید برابر با `JudgeResult::SUCCESS` باشد، مقدار `stderr` باید برابر با رشتهی خالی باشد و مقدار `stdout` باید برابر با خروجی برنامه باشد.
+ اگر اجرای برنامه در زمان مشخصشده خاتمه یابد و دادهای توسط برنامه در `stderr` نوشته شده باشد، مقدار `status` باید برابر با `JudgeResult::RUNTIME_ERROR` باشد، مقدار `stderr` باید برابر با محتویات `stderr` باشد و مقدار `stdout` باید برابر با رشتهی خالی باشد.
+ اگر اجرای برنامه در زمان مشخصشده خاتمه نیابد، فارغ از این که دادهای توسط در `stdout` یا `stderr` نوشته شده باشد، مقدار `status` باید برابر با `JudgeResult::TIME_LIMIT_EXCEEDED` باشد و مقدار `stderr` و `stdout` باید برابر با رشتهی خالی باشد.
## جاب `JudgeSubmission`
این جاب شناسهی یک `Submission` را دریافت میکند. وظیفهی این جاب، داوری یک `Submission` است. متد `handle` این جاب را طوری پیادهسازی کنید که ابتدا مقدار ستون `status` مربوط به `Submission` را به `judging` تغییر دهد، سپس داوری را با استفاده از کلاس `InputOutputJudge` انجام دهد و در نهایت، `status`، `stderr` و `stdout` را مقداردهی کند و تغییرات را در دیتابیس ذخیره کند.
## احراز هویت
پکیج `laravel/sanctum` در این پروژه نصب شده و از آن میتوانید برای احراز هویت استفاده کنید. توکنهای احراز هویت هنگام داوری توسط این پکیج تولید میشوند. هنگام ارسال درخواستها، مقدار `header` برابر با یک توکن `Bearer` قرار داده میشود و مقدار هدر `Accept` برابر با `application/json` خواهد بود.
%align_right_start%
## *endpoint* های *API*
%align_end%
در این برنامه باید سه *endpoiont* بهصورت زیر تعریف کنید. همهی این *endpoint* باید نیازمند احراز هویت کاربر باشند. در غیر اینصورت، کد پاسخ `401` باید برگردانده شود:
+ `GET /api/submissions`: با ارسال این درخواست، کاربر باید لیست ارسالهایش را در قالب یک آرایه ببیند (باید کالکشنی از `Submission`ها را به *JSON* تبدیل کرده و آن را برگردانید). کد پاسخ باید برابر با `200` باشد.
+ `POST /api/submissions`: با ارسال این درخواست، باید یک `Submission` به `Submission`های کاربر اضافه شود. **تضمین میشود** که مقادیر `code`، `time_limit` و `stdin` در بدنهی درخواست موجود خواهند بود. همچنین، جاب `JudgeSubmission` مربوط به `Submission` ارسالشده باید به صفی با عنوان `judge` اضافه شود. کد پاسخ باید برابر با `201` باشد.
+ `GET /api/submissions/{submission_id}`: با ارسال این درخواست، کاربر باید اطلاعات `Submission` با شناسهی `submission_id` را مشاهده کند (باید آبجکت `Submission` را به *JSON* تبدیل کرده و آن را برگردانید). اگر `Submission` وجود نداشته باشد یا متعلق به کاربر نباشد، کد پاسخ باید برابر با `404` باشد. در غیر اینصورت، کد پاسخ باید برابر با `200` باشد.
# نکات
+ داوری در یک سیستم لینوکسی صورت میگیرد. بنابراین میتوانید از امکانات خط فرمان آن استفاده کنید.
+ شما تنها مجاز به اعمال تغییرات در پوشههای `app` و `routes` هستید (میتوانید فایلهای جدیدی نیز در این پوشهها ایجاد کنید).
# آنچه باید آپلود کنید
پس از پیادهسازی موارد خواستهشده، پوشههای `app` و `routes` را زیپ کرده و آپلود کنید.