Skip to content
This repository was archived by the owner on Apr 28, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
# Laravel Coding Test
Please follow the following steps to start using the application:

You should have been told which set of tasks to complete. If not please let your contact know.
1. run `composer install`
2. Copy .env.example to .env
3. Run `php artisan key:generate`
4. Setup the database and mail configurations at .env file
5. Run `php artisan migrate`
6. Run `php artisan serve`
7. Access user register page http://localhost:8000/users/create

Feel free to do both sets if you want.

## Backend
## Backend Tasks

1. Add a page for users to register
2. Use http://postcodes.io/ to ensure that users submit a valid postcode
3. Send a welcome email when a user is registered
4. Add an artisan command to list recently registered users

## Frontend

Start the development server using `php artisan serve` and go to http://127.0.0.1:8000/address

1. Make the address lookup component accessible
2. Style it using bootstrap
- Command `php artisan users:latest`
- You can pass optional argument for command to get the desired number of users `php artisan users:latest 3`.
- If no argument passed to the command it will display the default count which is `10`.
37 changes: 37 additions & 0 deletions app/Abstracts/AbstractActionService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace App\Abstracts;

use App\Contracts\RepositoryInterface;

/**
* Description of AbstractActionService
*
* @author Mohamed Shehata<m.shehata.alex@gmail.com>
*/
abstract class AbstractActionService
{

/**
*
* @var RepositoryInterface
*/
protected $repository;

/**
* Create new instance
*
* @param RepositoryInterface $repository
*/
public function __construct(RepositoryInterface $repository)
{
$this->repository = $repository;
}

/**
* Performs the service action
*
* @param array $data The data used in executing the service action.
*/
abstract public function execute($data = []);
}
72 changes: 72 additions & 0 deletions app/Abstracts/AbstractMySQLRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

namespace App\Abstracts;

use App\Contracts\RepositoryInterface;
use Illuminate\Database\Eloquent\Model;

/**
* Description of AbstractMySQLRepository
*
* @author Mohamed Shehata<m.shehata.alex@gmail.com>
*/
abstract class AbstractMySQLRepository implements RepositoryInterface
{

/**
* @var Model The model which query will be run against.
*/
protected $model;

/**
* @param Model $model
*/
public function __construct(Model $model)
{
$this->model = $model;
}

/**
* @inheritDoc
*/
public function save(array $attributes): Model
{
return $this->model->create($attributes);
}

/**
* @inheritDoc
*/
public function count(): int
{
return $this->model->count();
}

/**
* @inheritDoc
*/
public function latest(): self
{
$this->model = $this->model->latest();

return $this;
}

/**
* @inheritDoc
*/
public function limit(int $limit): self
{
$this->model = $this->model->limit($limit);

return $this;
}

/**
* @inheritDoc
*/
public function get(array $columns = ['*']): \Illuminate\Database\Eloquent\Collection
{
return $this->model->get($columns);
}
}
43 changes: 43 additions & 0 deletions app/Console/Commands/ListRecentlyRegisteredUsers.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace App\Console\Commands;

use App\Services\RecentUsersListService;
use Illuminate\Console\Command;

class ListRecentlyRegisteredUsers extends Command
{
const USER_COUNT = 10;
const DISPLAY_COLUMNS = ['id', 'name', 'email', 'postcode', 'created_at'];
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'users:latest {count?}';

/**
* The console command description.
*
* @var string
*/
protected $description = 'List the recently registered users';


/**
* Execute the console command.
*
* @return int
*/
public function handle(RecentUsersListService $recentUserService)
{
$usersCount = $this->argument('count') ?? self::USER_COUNT;
$users = $recentUserService->execute([
'columns' => self::DISPLAY_COLUMNS,
'count' => $usersCount
]);
$this->table(array_map('ucfirst', self::DISPLAY_COLUMNS), $users->toArray());

return 0;
}
}
48 changes: 48 additions & 0 deletions app/Contracts/RepositoryInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace App\Contracts;

/**
*
* @author Mohamed Shehata<m.shehata.alex@gmail.com>
*/
interface RepositoryInterface
{
/**
* Store model in database and return the instance.
*
* @param array<string, mixed> $attributes
* @return \Illuminate\Database\Eloquent\Model
*/
public function save(array $attributes): \Illuminate\Database\Eloquent\Model;

/**
* Count existed models in database
*
* @return int
*/
public function count(): int;

/**
* Alias for order by `created_at`
*
* @return self
*/
public function latest(): self;

/**
* Limit query result count
*
* @return self
*/
public function limit(int $limit): self;

/**
* Get query results
*
* @param array $columns Columns to select from database
* @return \Illuminate\Database\Eloquent\Collection
*/
public function get(array $columns = ['*']): \Illuminate\Database\Eloquent\Collection;

}
2 changes: 1 addition & 1 deletion app/Exceptions/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function report(Throwable $exception)
* @return Response
* @throws Throwable
*/
public function render($request, Throwable $exception): Response
public function render($request, Throwable $exception)
{
return parent::render($request, $exception);
}
Expand Down
52 changes: 52 additions & 0 deletions app/Http/Controllers/UserController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace App\Http\Controllers;

use App\Http\Requests\StoreUserRequest;
use App\Services\UserSavingService;
use Exception;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\View;

/**
* Description of UserController
*
* @author Mohamed Shehata<m.shehata.alex@gmail.com>
*/
class UserController extends Controller
{
/**
* Display Form for creating new user.
*
* @return \Illuminate\Contracts\View\View
*/
public function create(\App\Services\UserCountingService $countingService)
{
$userCount = $countingService->execute();
return View::make('users.create', compact('userCount'));
}

/**
* Store new user in storage system.
*
* @param StoreUserRequest $request
* @param UserSavingService $savingService
*
* @return \Illuminate\Http\RedirectResponse
*/
public function store(
StoreUserRequest $request,
UserSavingService $savingService
)
{
try {
$savingService->execute($request->validated());
return redirect()->back()->with('success', __('messages.user.create_success'));
} catch (Exception $exc) {
Log::alert('Failed to store user', [
'error_message' => $exc->getMessage()
]);
}
return redirect()->back()->with('error', __('messages.user.create_failed'));
}
}
34 changes: 34 additions & 0 deletions app/Http/Requests/StoreUserRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace App\Http\Requests;

use App\Rules\PostCode;
use Illuminate\Foundation\Http\FormRequest;

class StoreUserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}

/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => ['required', 'string', 'max:255'],
'postcode' => ['bail', 'required', 'string', 'max:100', new PostCode()],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'string', 'min:6', 'confirmed'],
];
}
}
38 changes: 38 additions & 0 deletions app/Notifications/WelcomeNewUserEmail.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class WelcomeNewUserEmail extends Notification
{
use Queueable;

/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['mail'];
}

/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->line('Dear ' . $notifiable->name . ',')
->line('Welcome to '. config('app.name'))
->action('Visit us', url('/'))
->line('Thank you for using our application!');
}
}
18 changes: 18 additions & 0 deletions app/Repositories/UserRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
namespace App\Repositories;

use App\Abstracts\AbstractMySQLRepository;
use App\User;

/**
* Description of UserRepository
*
* @author Mohamed Shehata<m.shehata.alex@gmail.com>
*/
class UserRepository extends AbstractMySQLRepository
{
public function __construct(User $model)
{
parent::__construct($model);
}
}
Loading