داود مسئول مدیریت اطلاعات دانشجویان رشته مهندسی نرمافزار است و باید اطلاعات این دانشجویان را اصلاح کرده و در قالب جدیدی برگرداند.
اطلاعات دانشجویان در قالب *JSON* در فایلی به نام `students.json` قرار دارد و به ازای هر دانشجو، شماره دانشجویی، تاریخ تولد میلادی و نام ذخیره شده است.
شما باید این فایل را بخوانید و آن را به شکل زیر به یک لغتنامه (*dictionary*) از نوع *JSON* تبدیل کنید.
+ کلید هر عنصر این فایل، شماره دانشجویی کاربر شده و مقدار شماره دانشجویی (فیلد id) از داخل عناصر حذف شود.
+ سن دانشجو از روی تاریخ تولد استخراج شود و در کلید `age` ذخیره شود. دقت کنید که سن دانشجو تا روز برگزاری مسابقه (یعنی 4 اکتبر 2019) محاسبه شود.
+ حرف اول تمامی کلمات در فیلد `name` بزرگ و باقی حرفها کوچک شوند.
+ در صورتی که یک شماره دانشجویی چند بار در فایل *JSON* اولیه تکرار شده بود، شما باید اولین تکرار آن شماره را در نظر بگیرید و دیگر تکرارها را حذف کنید.
لغتنامه جدید را در قالب *JSON* در فایل جدیدی با نام `students_fixed.json` ذخیره کنید.
# مثال
مثلا برای فایلی با محتوای زیر:
```json
[{"id":9830011,"bdate":"1994\/9\/24","name":"sardar azmoun"},{"id":9864656,"bdate":"1992\/5\/14","name":"elon MUSK"}]
```
فایل جدیدی با خروجی زیر باید تولید شود:
```json
{"9830011":{"bdate":"1994\/9\/24","name":"Sardar Azmoun","age":"25"},"9864656":{"bdate":"1992\/5\/14","name":"Elon Musk","age":"27"}}
```
## آنچه باید آپلود کنید
فایل پاسخ خود را با نام `fixer.php` ذخیره کرده و به صورت فایل *ZIP* آپلود کنید.
مدیریت دانشجویان
در این سوال از شما خواسته شده تا اطلاعات مفید را از متن استخراج کنید و یا تغییراتی بر روی آن اعمال نمایید.
به این منظور شما باید سه تابع با مشخصات زیر بنویسید که هر کدام یک ورودی متنی دارد و پاسخ درخواستی را برمیگرداند:
1. تابع `findPhoneNumbers`: این تابع شماره تلفنهای همراه موجود در متن را تشخیص داده و در قالب یک آرایه برمیگرداند. شمارههای همراه دو حالت دارد:
* با `09` شروع شده و دارای ۱۱ رقم است.
* با `+9891` شروع شده و دارای ۱۳ کاراکتر است.
مثالی از ورودی و خروجی این تابع:
```php
"In shomareye mane: 09101007567 vali behtare +989101007567 ro save koni. In 9111231234 va0914513 kar nemikonan."
```
```php
["09101007567","+989101007567"]
```
2. تابع `findHashtags`: تمامی کلماتی که با علامت `#` شروع میشوند و تنها شامل اعداد و حروف انگلیسی است را تشخیص داده و در قالب یک آرایه از هشتگها برمیگرداند. این هشتگها باید شامل حداقل دو حرف به غیر از `#` باشند.
مثالی از ورودی و خروجی این تابع:
```php
"Salam #goodMOrning khoobi#to #4yourlove #bi-man"
```
```php
["#goodMOrning","#4yourlove"]
```
3. تابع `boldEmails`: این تابع تمامی ایمیلهای موجود در متن را درشتدانه (بولد) میکند. این تابع تنها ایمیلهایی که منطبق بر فرمت username@domain.tld هستند را پشتیبانی میکند که:
* نام کاربری تنها میتواند از کاراکتر انگلیسی، عدد، آندرلاین و نقطه تشکیل شود.
* دامنه تنها میتواند از کاراکتر انگلیسی و یا عدد تشکیل شود.
* tld یک کلمه سه حرفی از کاراکترهای انگلیسی است.
* کاراکترهای انگلیسی میتواند کوچک یا بزرگ باشد
مثالی از ورودی و خروجی این تابع:
```php
"Soalatono az info_test@Quera.ir ya info@Quera123.com ya test_#23@alaki.core beporsid"
```
```php
"Soalatono az info_test@Quera.ir ya <b>info@Quera123.com</b> ya test_#23@alaki.core beporsid"
```
## نکات عمومی
**نکته ۱**: برای سرعت بخشیدن به حل این مسئله، میتوانید از عبارات منظم به کمک توابع `preg_grep` و `preg_replace` و یا موارد مشابه استفاده کنید و برای یادگیری قوانین عبارات منظم میتوانید از [اینجا](https://regexone.com/) یا [اینجا](https://regex101.com) کمک بگیرید.
**نکته ۲**: تمامی موارد درخواستی (شماره همراه/ایمیل/هشتگ) با فاصله از دیگر کلمات جدا شدهاند و در غیر این صورت، نباید در نظر گرفته شوند.
**نکته ۳**: ترتیب اعضا در آرایه خروجی شما مهم نیست.
## آنچه باید آپلود کنید
هر سه تابع خود را در یک فایل با نام `functions.php` قرار دهید و به صورت فایل `.zip` آپلود کنید.
عبارات منظم
امیر کارآموز پیاچپی در کوئرا است. اخیرا طراحی یکی از سیستمهای کوئرا را به امیر سپردن اما از آنجایی که این پروژه، اولین پروژه بزرگ امیر هستش، در عیبیابی پروژه دچار مشکل شده است.
امیر از محمدمهدی که از برنامهنویسای قدیمی کوئراست راهنمایی خواست و محمدمهدی هم به امیر توضیح داد که لاگ و سیستم ثبت لاگ چیه و پیشنهاد داد که امیر برای پروژهاش یک سیستم ثبت لاگ طراحی کند و برای عیبیابی از آن استفاده کند.
محمدمهدی یک پروژه اولیه هم برای طراحی سیستم ثبت لاگ به امیر ارسال کرد و به او گفت که اگر میخواهد سیستم ثبت لاگ خوبی داشته باشد، باید این پروژه را کامل کند.
اما از آنجایی که امیر هنوز به پیاچپی مسلط نیست، از شما برای انجام این کار کمک خواسته است.
# پروژه اولیه
پروژه اولیه را از
[اینجا](%q_initial_url%)
دانلود کنید.
ساختار فایلهای این پروژه به صورت زیر است.
```
logger
├── LogException.php
├── Logger.php
└── LoggerInterface.php
```
# جزئیات
### کلاسها
#### `\Quera\LoggerInterface`
این کلاس `interface`ای است که کلاس `Logger` باید آن را پیادهسازی کند.
این کلاس از `PHP Standards Recommendations` برداشته شدهاست.
#### `\Quera\Logger`
این کلاس کلاسِ اصلی ما است که دارای توابع زیر است:
- تابع کانستراکتور : دو ورودی میگیرد که اولی آدرسی است که لاگ باید در آن ذخیره شود و دومی گزینههای مرتبط با روش ذخیره است.
آدرس میتواند لینک یک `API` یا آدرس کامل یک فایل باشد که از طریق `type` در گزینهها مشخص میشود.
آرایهی گزینهها میتواند شامل موارد زیر باشد:
> `type`: نشاندهنده نوع ذخیره لاگ هست که میتواند `file` یا `api` باشد و در صورتی که هیچکدام از این دو نباشد تابع باید یک خطا از نوع `LogException` پرتاب کند.
> `dateFormat`: فرمت مورد نظر برای درج تاریخ در لاگ را نشان میدهد و از نوع [فرمت استاندارد در پیاچپی]( https://www.php.net/manual/en/datetime.formats.php
) است.
> `logFormat`: فرمت مورد نظر برای ذخیره لاگ را نشان میدهد. این فرمت میتواند دارای متغیرهایی باشد که میتوانند `date`, `level` و `message` باشند یا از طریق متغیر `context` در توابع لاگ ارسال شوند. همچنین ممکن است در `message` نیز مانند `logFormat` متغیر وجود داشته باشد.
`date` با تاریخ با فرمت مورد نظر در `dateFormat` جایگزین میشود. `level` با اهمیت لاگ با حروف بزرگ جایگزین میشود. `message` با پیام ارسالی هنگام فراخوانی توابع لاگ جایگزین میشود. متغیرها با علامت `{}` دورشان مشخص میشوند.
> `threshold`: نشاندهنده حداقل اهمیت مورد نیاز لاگ برای ذخیره شدن است. اهمیت لاگها به ترتیب از زیاد به کم از چپ به راست بهصورت `EMERGENCY, ALERT, CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG` است. اگر برابر `ALL` قرار داده شود همه سطوح را ذخیره میکند. اگر مقداری متفاوت از ۹ مقدار ذکر شده قرار داده شود، باید یک خطا از نوع `LogException` پرتاب کند.
> `append`: در صورت انتخاب شدن نوع `file`، اگر مقدار این متغیر برابر `true` باشد، لاگها را به انتهای فایل مشخص شده اضافه میکند و در غیر این صورت از ابتدای فایل شروع به نوشتن لاگها میکند. مقدار append تنها در ابتدا برای اولین بار نوشتن در فایل اهمیت دارد.
در صورتی که هرکدام از متغیرها در آرایه ورودی وجود نداشته باشد، مقدار آن از روی جدول زیر باید تعیین شود.
|کلید |مقدار پیشفرض در صورت تعیین نشدن |
|-----------|:-----------------------------------|
| type | `'file'` |
| dateFormat| `'Y-m-d H'` |
| logFormat | `'[{date}]-[{level}]-{message}'` |
| threshold | `'ALL'` |
| append | `true` |
- توابع موجود در `LoggerInterface` که شامل `emergency`, `alert`, `critical`, `error`, `warning`, `notice`, `info`, `debug` که هر کدام یک لاگ با سطح متفاوت ذخیره میکنند، میباشد.
علاوه بر آنها تابع `log` نیز مانند آنها وجود دارد با این تفاوت که سطح لاگ را با حروف بزرگ به عنوان ورودی دریافت میکند.
با صدا شدن هرکدام ازین ۹ تابع مرتبط به لاگ، باید لاگ در همان زمان طبق گزینههای انتخاب شده در کانستراکتور کلاس، در فایل ذخیره شده یا به فرمت `POST` به `API` ارسال شود.
#### `\Quera\LogException`
این کلاس خطایی است که در صورت وجود اشتباه در آپشنهای ورودی سازنده کلاس `Logger` پرتاب میشود.
## مثال
یک نمونه از اجرای این کلاس به شکل زیر است:
```php
$options = [
'dateFormat' => 'Y-m-d H',
'logFormat' => '[{date}] {level}-{message}',
'threshold' => 'ERROR',
'append' => true,
];
$logger = new Quera\Logger("~/debug.log", $options);
$logger->critical('oh oh');
$logger->emergency('system is down');
$logger->notice('change variable name');
$logger->log('ERROR', 'error in line 2');
$logger->error('error in line {variable1}', ['variable1' => '6']);
/* file ~/debug.log:
[2019-10-03 10] CRITICAL-oh oh
[2019-10-03 10] EMERGENCY-system is down
[2019-10-03 10] ERROR-error in line 2
[2019-10-03 10] ERROR-error in line 6
*/
```
# نکات
- پروژه اولیه دارای ۳ فایل است که شما تنها میتوانید در فایل `Logger.php` تغییرات ایجاد کنید.
- کلاس `Logger` لزوما باید `LoggerInterface` را پیادهسازی کند.
- `TimeZone` را `Asia/Tehran` قرار دهید.
- در هنگام ارسال به `API` پیام باید با عنوان `message` ارسال شود، به شکلی که با استفاده از `$_POST['message']` قابل دسترسی باشد و برای مثال با فرمت اولیه، اگر به شکل
```php
$logger->emergency('something went wrong...')
```
فراخوانی شده باشد محتوای `$_POST['message']` به شکل
```
[2019-10-02 21]-[EMERGENCY]-something went wrong...
```
دریافت میشود.
# بارگذاری
در انتها فایل های زیر را به صورت یک فایل `zip` آپلود نمایید. نام فایل `zip` اهمیتی ندارد.
```
[your-zip-file-name].zip
├── LogException.php
├── Logger.php
└── LoggerInterface.php
```
سیستم واقعهنگار!
یکی از فروشگاههای زنجیرهای قصد دارد طراحی پلتفرم فروشگاه اینترنتی خود را به شما بسپارد.
نیازمندیهای نرم افزار این مجموعه شامل مدیریت محصولات فروشگاه و سبد خرید کاربران می باشد.
برای پیاده سازی به این نکات توجه کنید:
+ کاربران برای خرید محصولات نیازی به احراز هویت ندارند.
+ در حال حاضر برای ایجاد محصول جدید نیازی به کنترل سطح دسترسی نداریم. به عبارت دیگر، کلیه کاربران امکان ایجاد محصول جدید را دارند.
+ کاربران از هر محصول فقط به تعداد مشخصی می توانند در سبد خرید خود اضافه کنند. میزان محدودیت خرید برای هر محصول در هنگام تعریف آن مشخص می شود.
## ساختار پروژه
ساختار اولیه نرم افزار با استفاده از مدل `MVC` پیاده سازی شده است و به صورت زیر می باشد:
+ دایرکتوری `Controllers`: کلاسهای کنترلر داخل این دایرکتوری قرار گرفته اند. تمامی این کلاسها تحت فضای نام (namespace) `Controllers` قرار دارند. توجه کنید که شما مجاز به تغییر هیچ کدام از کلاسهای کنترلر نیستید.
+ دایرکتوری `Models`: کلاسهای مدل که مسئول ارتباط با پایگاه داده هستند، داخل این دایرکتوری قرار گرفته اند. تمامی این کلاسها تحت فضای نام (namespace) `Models` قرار دارند.
+ دایرکتوری `Views`: فایلهای نمایش برنامه در این دایرکتوری قرار می گیرند.
+ فایل `index.php`: نقطه ورودی و مدیریت کلیه درخواستها در سیستم.
+ فایل `helpers.php`: این فایل مجموعهای از توابع کمکی می باشد.
+ فایل `autoload`: کلیه توابع مربوط به بارگذاری خودکار فایلهای `php` مورد نیاز، در این فایل قرار می گیرند.
# آدرس دهی
کلیه درخواستها به فایل `index.php` ارجاع می شوند.
سپس با استفاده از آدرس `URL`، کنترلر متناسب برای مدیریت درخواست فراخوانی میشود.
کنترلر و متد درخواست شده با استفاده از متغیرهای زیر و به صورت `Query String` در آدرس صفحه قرار میگیرند.
+ `controller` : نام کنترلر موجود در دایرکتوری `Controllers` به صورت `lowercase` در این متغیر قرار می گیرد
+ `method`: نام متد فراخوانی شده در این متغیر قرار می گیرد.
##### نمونه :
برای فراخوانی متد `index` از کنترلر `Products` آدرس دهی باید به صورت زیر باشد:
`http://test.dev/index.php?controller=products&method=index`
** `test.dev` یک دامنه فرضی می باشد.
## پایگاه داده
برای ذخیره سازی اطلاعات می خواهیم از پایگاه داده `Sqlite3` استفاده نماییم.
برای این منظور نیاز به دو جدول با ساختار زیر داریم.
##### 1. جدول محصولات (`products`)
| ملاحضات | تعریف | نوع | اسم ستون |
|:-:|:-:|:-:|:-:|
| `PRIMARY KEY` | شناسهی محصول | `integer` | `id` |
| `NOT NULL UNIQUE` | عنوان محصول | `text` | `title` |
| `NOT NULL` | قیمت محصول | `integer` | `price` |
| `NOT NULL` | حداکثر تعداد قابل خریداری برای هر شخص | `integer` | `maximum_count` |
##### 2. جدول سبد خرید (`carts`)
| ملاحضات | تعریف | نوع | اسم ستون |
|:-:|:-:|:-:|:-:|
| `PRIMARY KEY` | شناسه | `integer` | `id` |
| `NOT NULL` | شناسه سبد خرید کاربر | `text` | `cart_identifier` |
| `NOT NULL` | شناسه محصول خریداری شذه | `integer` | `product_id` |
| `NOT NULL` | تعداد محصول خریداری شده | `integer` | `product_count` |
شما باید فایل دیتابیس را به نام `db.sqlite` در دایرکتوری اصلی، در کنار فایل `index.php` ایجاد نمایید.
## پروژه اولیه
پروژه اولیه را میتوانید از [اینجا](https://quera.ir/qbox/download/czj7wMTymN/init.zip) دانلود کنید.
## پیاده سازی
#### 3. پیاده سازی توابع کمکی
در فایل `helpers.php` نیار به پیاده سازی چند تابع داریم.
+ `resolve_request_parameter($name, $default = null)`
این تابع را به گونهای پیاده سازی نمایید که بتواند مقدار `$name` را در متغیر `$_GET` یا `$_POST` جستحو نماید و مقدار آنرا برگرداند.
در صورتی که این متغیر یافت نشد، باید مقدار `$default` بازگردانده شود.
+ `redirect($controller = '', $method = '')`
این تابع را به گونهای پیاده سازی نمایید که با دریافت نام کنترلر و متد اکشن مورد نیاز، ابتدا آدرس صحیح را ایجاد کرده.
سپس کاربر را به آن آدرس ***redirect*** نماید.
+ `set_alert($alert)`
این تابع را به گونهای پیاده سازی نمایید که رشته دریافت شده را به گونهای در سیستم ذخیره نماید، که این متغیر تنها یک بار بعد از ***redirect*** به کاربر نمایش داده شود.
برای این کار می توانید از `Session` کمک بگیرید.
+ `get_alert()`
این تابع را به گونه ای پیاده سازی نمایید که پیام ثبت شده توسط `set_alert` را برای نمایش در فایلهای `view` بازگرداند.
توجه کنید که هر پیام تنها یکبار قابلیت نمایش دارد و در صورت ***refresh*** کردن صفحه، هیچ پیامی نباید نمایش داده شود.
همجنین در صورتی که هیچ پیامی تنظیم نشده بود، باید مقدار `null` برگشت داده شود.
+ `get_cart_identifier()`
این تابع را به گونهای پیاده سازی نمایید که یک شناسه یکتا برای هر کاربر ایجاد نموده و مقدار آن را بازگرداند.
محصولات موجود در سبد خرید هر کاربر با استفاد از این شناسه، تفکیک می شود.
توجه کنید که برای هر کاربر تنها یکبار شناسه صادر می شود و این شناسه باید در تمام مراحل بعدی خرید بدون تغییر باقی بماند.
برای این کار می توانید از `Session` کمک بگیرید.
مقدار بازگشتی این تابع باید یک رشته از اعداد یا حروف 5 حرفی باشد.
#### 1. پیاده سازی ساختار autoload
در هیچ کدام از کلاسها نباید از روشهای بارگزاری مستقیم فایل (شامل require, include, require_once, include_once) استفاده نمود.
شما باید با استفاده از متدهای پیش بینی شده در `php` برنامه را به گونهای تغییر دهید که در صورت نیاز به هر کلاس، برنامه به طور خودکار فایل مربوط به آن کلاس را بارگذاری نماید.
توجه کنید که نام `namespace` ها برابر نام دایرکتوری و نام `class` ها برابر نام فایل می باشد.
توابع پیاده سازی شده را در فایل `autoload.php` قرار دهید.
#### 2. پیاده سازی ساختار مدلها
همه کلاسهای مدلها از کلاس `DatabaseModel` ارث بری می کنند.
شما باید منطق این کلاس را به گونهای پیاده سازی نمایید که عملیاتهای زیر را به درستی انجام دهد.
+ `getConnection()`
این متد باید یک شی از توع `SQLite3` که به دیتابیس متصل شده است را برگرداند.
+ `$attributes`
با استفاده از توابع پیش بینی شده در `php` این کلاس را به گونهای توسعه دهید، که متغیرهای جدید منتسب شده را در آرایه `$attributes` قرار دهد.
همچنین در صورت دسترسی به متغیرهای ثبت نشده در کلاس، سعی کند نام متغیر را از آرایه `$attributes` بخواند.
نمونه:
```php
$product = new \Models\Product();
$product->title = "Pencil";
$product->price = 10000
```
بعد ار اجرای این کد متغیر `$attributes` باید به صورت آرایه ای شامل عناصر زیر باشد:
```php
[
"title": "Pencil",
"price": 10000",
]
```
همجنین در صورت فراخوانی متغیرهای دیگر، آرایه `$attributes` باید مورد جستجو قرار گیرد.
```php
echo $product->title;
echo $product->price;
```
خروجی این کد باید به صورتزیر باشد:
```text
"Pencil"
10000
```
ضمنا توجه کنید که اگر در هنگام نمونه سازی از کلاس `Model` ها، لیستی از مقادیر را به تابع سازنده ارسال کنیم، این مقادیر در آرایه `$attributes` قرار می گیرند.
#### 3. نصب سیستم
برای نصب سیستم باید به آدرس `index.php?controller=install&method=index` مراجعه نمایید.
نصب سیستم شامل پروسه ایجاد جداول پایگاه داده می باشد.
تابع `buildSchema` در هر کلاس مدل مسئولیت ایجاد جدول مربوط به خود را دارد.
تابع `buildSchema` موجود در کلاس `\Models\Product` و `\Models\Cart` را به گونهای تغییر دهید که بعد از فراخوانی جداول مورد نیاز را ایجاد کند.
#### 4. نمایش لیست محصولات
برای نمایش لیست محصولات باید به آدرس `index.php?controller=products&method=index` مراجعه نمایید.
برای نمایش صحیح محصولات، متد `all` از کلاس `Models\Product` را به گونهای تغییر دهید که یک آرایه شامل لیستی از instance های `Models\Product` را با مقادیر درست بازگرداند.
هر کدام از instance های محصولات باید شامل مقادیر زیر باشد:
```php
$product->id
$product->title
$product->price
$product->maximum_count
```
محصولات باید به ترتیب درج شدن، روی لیست نمایش داده شود.
توجه کنید که شما مجاز به تغییر کنترلر `Controllers\Products` نمی باشید.
تمامی تغییرات باید روی مدل `Models\Product` اعمال گردد.
#### 5. افزودن محصول جدید
برای نمایش فرم محصول جدید باید به آدرس `index.php?controller=products&method=form` مراجعه نمایید.
متد `create` از مدل `Models\Product` را به گونهای تغییر دهید، که با دریافت پارامترهای نوشته شده، اطلاعات محصول جدید را در دیتابیس ذخیره نماید.
سپس یک نمونه از `Models\Product` را با کلیه اطلاعات محصول ثبت شده، شامل `id`, `title`, `price`, `maximum_count` باز گرداند.
#### 6. دریافت اطلاعات محصول
متد `find` از کلاس `Models\Product` را به گونه ای پیاده سازی نمایید، که با دریافت `id` محصول، کلیه اطلاعات محصول را از دیتابیس گرفته و به صورت یک شی `Models\Product` بازگرداند.
در صورتی که هیچ محصولی با `id` ذکر شده یافت نشد، سیستم باید یک `Exception` با پیغام `Model Not Found` پرتاب نماید.
#### 7. خرید محصول
در لیست محصولات با کلیک بر روی لینک `Buy` به آدرس `index.php?controller=carts&method=add` هدایت می شوید.
شما باید تابع `purchase` از مدل `Models\Cart` را به گونهای پیاده سازی نمایید که با دریافت شناسه سبد خرید کاربر و یک شی `Models\Product` به عنوان ورودی اقدامات زیر را انجام دهد:
+ در صورت که کاربر قبلا از این محصول در سبد خود اضافه نکرده بود، این محصول را به سبد خرید کاربر اضافه نماید.
+ در صورتی که کاربر قبلا این محصول را به سبد خرید خود اضافه کرده بود، باید مقدار `product_count` آن یک واحد افزایش یابد.
+ در صورتی که کاربر قبلا حداکثر مقدار قابل سفارش از این محصول را به سبد خود اضافه کرده بود، باید یک `Exception` با پیام `Maximum Count Reached` پرتاب شود.
#### 8. نمایش محصولات موجود در سبد خرید
برای نمایش لیست محصولات سبد خرید کاربر باید به آدرس `index.php?controller=carts&method=index` مراجعه نمایید.
برای نمایش لیست سبد خرید باید تابع `getProducts` از کلاس `Models\Cart` را به گونه ای تغییر دهید که یک آرایه شامل نمونههایی از کلاس `Models\Cart` را بازگرداند.
هر instance از کلاس `Models\Cart` باید شامل اطلاعات زیر باشد.
```php
$cart->id; // id ثبت شده در جدول carts
$cart->title; // عنوان محصول خریداری شده
$cart->price; // قیمت هر عدد از محصول خریداری شده ( قیمت FEE)
$cart->product_id; // id محصول انتخاب شده در حدول products
$cart->product_count; // تعداد سفارش از این محصول
$cart->total_price; // حاصل ضرب قیمت محصول در تعداد سفارش محصول
```
همچنین شما باید متد `calculateTotalPrice` را به گونه ای پیاده سازی نمایید که با دریافت لیست تولید شده توسط تابع `getProducts`، بتواند مجموع کل قیمت سفارش را محاسبه نماید.
#### 9. حذف یک محصول از سبد خرید
در لیست سبد خرید با کلیک روی لینک Add به متد `purchase` که قبلا پیاده سازی نمودیم، هدایت می شویم.
در صورت کلیک روی لینک Remove به آدرس `index.php?controller=carts&method=remove` هدایت می شویم.
در این مسیر، محصول انتخاب شده باید از سبد خرید به طور کلی حذف شود.
برای این کار باید متد `remove` از کلاس `Models\Cart` را به گونه ای تغییر دهید، که با دریافت شناسه سبد خرید کاربر و یک instance از `Models\Product`، اقدامات زیر را انجام دهد:
+ در صورتی که محصول در سبد خرید کاربر وجود داشت، از سبد خرید حذف می شود.
+ در صورتی که این محصول در سبد خرید کاربر وجود نداشت، یک `Exception` با پیام `Product Not Available` باید پرتاب شود.
## ارسال نتیجه
در نهایت فایلها و فولدرهای زیر را بدون تغییر ساختار فولدر بندی، در یک فایل ***zip*** ارسال نمایید.
```text
/Models/Cart.php
/Models/Product.php
/Models/DatabaseModel.php
/helpers.php
/autoload.php
```
فروشگاه زنجیرهای
# سامانه دانیال همراه
یکی از شرکت های معتبر واردات قعطات کامپیوتری (به نام دانیال همراه) هر ماه قطعاتی را از کشورهای مختلف وارد می کند. این شرکت اخیرا با مشکلات متعددی برای امور گارانتی خود مواجه شده است. روال گارانتی به این صورت است که الزاما بایستی شماره سریال قطعه مورد نظر توسط خریدار نهایی به شرکت اعلام شود. به همین دلیل شرکت برای هر دستگاه یک کارت گارانتی چاپ کرده که بر روی کارت شماره سریال قطعه و همچنین کد فعالسازی گارانتی چاپ شده است. کد فعالسازی به صورت مخفی و تحت یک `scratch code` بر روی کارت چاپ می شود.
حال می خواهیم سیستمی با مشخصات زیر برای ارائه کدهای گارانتی به مشتریان شرکت پیاده سازی کنیم.
## پروژه اولیه
فایل های پروژه اولیه را می توانید از [اینجا](https://quera.ir/qbox/download/OnbVnGdZQz/danial-hamrah.zip) دانلود کنید. این پروژه بر اساس کتابخانه لاراول نسخه `5.8.*` پیاده سازی خواهد شد.
فایل هایی که اضافه بر ساختار معمول پروژه های لاراول در اختیار شما قرار شده است عبارتند از :
- فایل `input.xlsx` که در مسیر `storage/app/input.xlsx` قرار داده شده است. این فایل از طرف شرکت به ما داده می شود و شامل دو ستون است. ستون اول یا `A` کد یکتای شناسایی محصولات (شبیه به شماره سریال) بوده و ستون دوم کد فعالسازی است که شرکت به صورت تصادفی برای هر قطعه مشخص کرده.
- فایل `CValidator.php` که در مسیر `app/CValidator.php` قرار داده شده است و شامل تابع اعبتارسنجی کد ملی است که برای قسمت اعتبارسنجی می توانید از آن استفاده کنید.
- فایل های مورد نیاز برای قسمت `Frontend` که در پوشه های `resource/views/home` و `public/css` قرار داده اند تا نیازی به توسعه `css/html` از سمت شما به حداقل ممکن برسد.
## لیست نیازمندی ها
- با توجه با اینکه مدیریت شرکت نسبت به امنیت سامانه بسیار حساس است، مخالفت بسیار زیادی با این پروژه داشته است. لذا به ایشان اطمینان دادیم که امنیت سامانه به نحوی تامین شود که اگر حتی کل فایل ها و پایگاه داده برنامه مورد حمله واقع شد اطلاعات کد های فعالسازی دچار مشکل و دستبرد نشود. به همین دلیل امکان استفاده مستقیم از فایل اکسل ورودی را نداریم. بایستی یک دستور `Artisan` به نام `GenerateFile` ایجاد کنید تا با فراخوانی این دستور یک فایل `csv` به این صورت ایجاد شود که هر سطر آن معادل یک سطر در فایل اکسل بوده با این تفاوت که فیلد شماره سریال ها (فیلد اول) به صورت `hash` شده با الگوریتم `sha1` ذخیره شود و فیلد دوم با استفاده از کلاس `Crypto` لاراول رمز نگاری شود. در اینصورت حتی اگر کل اطلاعات در دسترس نفر دیگری قرار بگیرد بدون داشتن شماره سریال ها به صورت `palin text` نمی تواند از کدهای گارانتی استفاده کند. برای انجام این قسمت در نظر داشته باشید که فایل `csv` با نام `data.csv` در آدرس `storage/app/data.csv` باید ساخته شود و کاراکتر `delimiter` حتما بایستی `,` باشد اما خط ابتدای فایل که نام `field` ها در آن نوشته می شود مورد داوری قرار نمیگیرد و مختارید که از هر نامی استفاده کنید ولی وجود آن الزامی است.
- لیست `route` های مورد نیاز در فایل `routes/web.php` از قبل نوشته شده است. برنامه به صورت یک فرم ۳ مرحله ای بوده و در هر مرحله اطلاعات مورد نیاز دریافت و اعتبارسنجی (`validate`) می شوند.
- در مرحله اول فیلدهای نام و نام خانوداگی صرفا خالی یا پر بودنشان ملاک بررسی است. هیچ شرط اضافه ای نخواهند داشت.
- فیلد کد ملی علاوه بر شرط پر بودن باید با توجه به تابع موجود در فایل `CValidator` مورد بررسی قرار گرفته تا از اعتبار اولیه آن مطمئن شویم.
- منظور از فیلد کد گارانتی همان فیلد `serial_number` در فایل اکسل است که علاوه بر شرط پر بودن باید در لیست ارائه شده نیز موجود باشد. یعنی مقدار وارد شده توسط کاربر را بررسی خواهیم کرد که آیا در محتویات ستون اول فایل اکسل وجود دارد یا نه؟. ضمنا این فیلد بایستی از نظر تعداد کاراکتر ها (که ۲ کاراکتر عددی می باشد) نیز مورد بررسی قرار بگیرد
- در صورتیکه کلیه اطلاعات به درستی وارد شد، کاربر را به صفحه دوم منتقل میکنیم و قبل از آن دو کلید در `session` ست می کنیم. اول کلید `gid` که همان کد گارانتی است که کاربر به درستی وارد کرده و دوم کلید `code` که محتوی کد موقتی است که برای اعتبار سنجی کاربر مورد استفاده قرار خواهد گرفت.
- کد موقت تولید شده قبلا از طریق پیامک به دست کاربر می رسید اما به دلیل شرایط مسابقه فعلا تصمیم گرفتیم که کد را مستقیما در مرحله دوم به کاربر نشان بدهیم.
- با ارسال فرم در مرحله دوم اولا بررسی خواهیم کرد که کلید های `session` که در مرحله اول ست شده بودند همچنان وجود داشته و مقادیر آنها معتبر باشند (در صورت عدم وجود کلید ها بایستی کاربر به مرحله قبل منتقل شود) و دوما مقدار `code` که کاربر وارد میکند با کدی که در مرحله ۱ تولید شده است یکسان و همخوان باشد. ضمنا فیلد `code` بایستی از نظر تعداد کاراکتر ها (که ۶ کاراکتر عددی می باشد) نیز مورد بررسی قرار بگیرد
- در صورتیکه کد موقت به درستی وارد شود، کاربر را به مرحله سوم منتقل میکنیم و کد فعالسازی متناظر با کد گارانتی وارد شده را رمزگشایی کرده و به او نمایش میدهیم.
- لطفا نکات زیر را برای داوری صحیح پاسخ هایتان در نظر داشته باشید :
-ملاک بررسی صحت اعتبارسنجی های انجام شده کلید `errors` در `session` می باشد که به صورت عادی توسط لاراول ست می شود. به بیان دیگر حتما سعی کنید از توابع و کلاس های موجود در لاراول برای اعتبارسنجی و تولید خطاهای احتمالی استفاده کنید تا کمترین میزان مغایرت با داوری را داشته باشید. به این منظور می توانید از قسمت [`Validation`](https://laravel.com/docs/5.8/validation) کمک بگیرید.
-پیغام های تولید شده در اعتبار سنجی ها کاملا باید مطابق لیست زیر باشد (واضح است که حتی ۱ کاراکتر اختلاف باعث رد شدن جواب خواهد شد) :
```
پر کردن فیلد نام اجباری می باشد
پر کردن فیلد نام خانوادگی اجباری می باشد
پر کردن فیلد کد ملی اجباری می باشد
کد ملی وارد شده صحیح نمی باشد
پر کردن فیلد کد گارانتی اجباری می باشد
کد گارانتی وارد شده معتبر نمی باشد
تعداد رقم های وارد شده برای کد گارانتی صحیح نمی باشد
پر کردن فیلد کد موقت اجباری می باشد
تعداد رقم های وارد شده برای کد موقت صحیح نمی باشد
کد موقت وارد شده صحیح نمی باشد
کد فعالسازی شما عبارت است از : 13
```
- در نظر داشته باشید که در طول برنامه پس از تولید فایل `data.csv` فایل ورودی اکسل اولیه حذف خواهد شد و دیگر در دسترس نخواهد بود. لذا در ادامه بایستی همه چیز با استفاده از اطلاعات فایل تولید شده بررسی و ارائه شود.
- برای استخراج اطلاعات از فایل اکسل می توانید از کتابخانه های آماده استفاده کنید و فایل `composer.json` را همراه پاسخ خود آپلود کنید.
- در صفحات مختلف، نسبت به مرحله ای که فعال می باشد، بایستی وضعیت نمایش داده شده در بالای فرم (که مراحل را به صورت فعال و غیر فعال نمایش می دهد) صحیح باشد. مثلا در صفحه ای که آدرس آن `/step2` است باید مراحل ۱ و ۲ هردو فعال باشند (کلاس `active` به عنصر `li` داده شود).
## آنچه باید آپلود کنید
پوشه های زیر را در یک فایل فشرده با فرمت `zip` آپلود کنید :
```
app
config
resources
routes
composer.json
```
### تصحیح
+ گویا فایل `input.xlsx` که در پروژه اولیه قرار داده شده با نرم افزار اکسل مشکل دارد و باز نمیشود. اول این که فایل از نظر محتوایی سالمه و با کدهای php میشه محتواش رو خوند و دوم اینکه میتونید همون فایل رو با google sheet باز کنید یا اینکه فایل جدید رو از این [آدرس](https://quera.ir/qbox/download/DF4BnTrsxQ/input.xlsx) دانلود کنید و جایگزین کنید.
+ در مرحله دوم در صورتیکه همه چیز صحیح باشد، بایستی کد فعالسازی به صورت رمزگشایی شده در یک متغیر `session` با کلید `activation_code` قرار بگیرد و کاربر به مرحله سوم منتقل شده و کد فعالسازی به کاربر نشان داده شود.