Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Booj code test submission for Drew Henry. #1

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
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
33 changes: 33 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_LOG_LEVEL=debug
APP_URL=http://localhost

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret

BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null

PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
5 changes: 5 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
* text=auto
*.css linguist-vendored
*.scss linguist-vendored
*.js linguist-vendored
CHANGELOG.md export-ignore
26 changes: 13 additions & 13 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
vendor/
node_modules/

# Laravel 4 specific
bootstrap/compiled.php
app/storage/

# Laravel 5 & Lumen specific
bootstrap/cache/
storage/
.env.*.php
.env.php
/node_modules
/public/hot
/public/storage
/storage/*.key
/vendor
/.idea
/.vagrant
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
.env
.env.example
/nbproject/private/
phpunit-*.phar
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,15 @@ Please use the [ORM](https://laravel.com/docs/5.2/eloquent) rather than crafting
* Deploy it for real so we can play with it! (and then tell us about how you deployed it)
* Handle image uploading while adding books to the list
* Do something fancy like integrating an external API or handling user authentication

<hr />

## Deployment
* Ensure that the *storage* and *bootstrap/cache* directories are writable, e.g. using **chmod o+w -R _folder_** command.
* If using sqlite, be sure to create the sqlite database, e.g. using the command **touch database/database.sqlite**.
* Copy the *.env.example* file to *.env* and change the configuration variables to suit your system. In particular, *APP_KEY* must be set, e.g. using the command **php artisan key:generate**.
* Images are stored in *storage/public*. In order for this images to be displayable, a link to this folder from the public directory must be made. This can be done using the command **php artisan storage:link**.
* Run the migrations and, optionally, the seeds to generate the database tables.

## Live Test
A live test can be found at (http://parasolarchives.com/site/booj/public). To login, the email address *[email protected]* with password *secret* can be used. Note that because this website runs on a shared host that does not support symbolic links, image uploading has been disabled.
196 changes: 196 additions & 0 deletions app/Book.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
<?php

namespace App;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\UploadedFile;
use Collective\Html\Eloquent\FormAccessible;
use Storage;

class Book extends Model
{
use FormAccessible;
use Traits\Sortable;

/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'books';

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['title', 'author', 'isbn13', 'publication_date', 'position'];

/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = [];

/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'user_id' => 'integer'
];

/**
* Cover image storage path.
*
* @static string
*/
public static $covers = 'public/covers';

/**
* Supported cover image types.
*
* @static array
*/
public static $coverExts = [
'gif',
'jpg',
'jpeg',
'png'
];

/**
* Create a new book instance.
*
* @param array $attributes
* @return User
*/
public function __construct(array $attributes = [])
{
$this->initializeMoves();

return parent::__construct($attributes);
}

/**
* Get book owner.
*
* @return User
*/
public function user()
{
return $this->belongsTo('App\User');
}

/**
* Get the book's publication date for forms.
*
* @param string $value
* @return string
*/
public function formPublicationDateAttribute($value)
{
return !empty($value) ? Carbon::parse($value)->format('Y-m-d') : null;
}

/**
* Set the book's publication date.
*
* @param string $value
* @return void
*/
public function setPublicationDateAttribute($value)
{
$this->attributes['publication_date'] = $value ?: null; # REPLACE EMPTY WITH NULL
}

/**
* Delete the model.
*
* @return void
*/
public function delete()
{
$this->deleteCover();
parent::delete();

$this->resort();
}

/**
* Save model.
*
* @param array $options
* @return void
*/
public function save(array $options = [])
{
$this->position = $this->position ?? $this->user->countBooks(); # DEFAULT POSITION IS LAST

parent::save($options);
}

/**
* Get cover image path.
*
* @return string|bool
*/
public function getCoverPath()
{
$ext = $this->hasCover();
if ($ext) {
return Storage::url(self::$covers . '/' . $this->id . '.' . $ext);
}

return false;
}

/**
* Check if book has a cover image stored and return the extension.
*
* @return string|bool
*/
public function hasCover()
{
foreach (self::$coverExts as $ext) {
if (Storage::exists(self::$covers . '/' . $this->id . '.' . $ext)) {
return $ext;
}
}

return false;
}

/**
* Delete cover image.
*
* @return bool
*/
public function deleteCover()
{
$ext = $this->hasCover();
if ($ext) {
return Storage::delete(self::$covers . '/' . $this->id . '.' . $ext);
}

return true;
}

/**
* Store cover image from form.
*
* @param string $file
* @return string|bool
*/
public function storeFormCover(UploadedFile $file)
{
if (!in_array($file->extension(), self::$coverExts)) { # INVALID EXTENSION
return false;
}
$this->deleteCover();
return $file->storeAs(self::$covers, $this->id . '.' . $file->extension());
}
}
40 changes: 40 additions & 0 deletions app/Console/Kernel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
//
];

/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
// $schedule->command('inspire')
// ->hourly();
}

/**
* Register the Closure based commands for the application.
*
* @return void
*/
protected function commands()
{
require base_path('routes/console.php');
}
}
65 changes: 65 additions & 0 deletions app/Exceptions/Handler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

class Handler extends ExceptionHandler
{
/**
* A list of the exception types that should not be reported.
*
* @var array
*/
protected $dontReport = [
\Illuminate\Auth\AuthenticationException::class,
\Illuminate\Auth\Access\AuthorizationException::class,
\Symfony\Component\HttpKernel\Exception\HttpException::class,
\Illuminate\Database\Eloquent\ModelNotFoundException::class,
\Illuminate\Session\TokenMismatchException::class,
\Illuminate\Validation\ValidationException::class,
];

/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* @param \Exception $exception
* @return void
*/
public function report(Exception $exception)
{
parent::report($exception);
}

/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
return parent::render($request, $exception);
}

/**
* Convert an authentication exception into an unauthenticated response.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Auth\AuthenticationException $exception
* @return \Illuminate\Http\Response
*/
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json(['error' => 'Unauthenticated.'], 401);
}

return redirect()->guest(route('login'));
}
}
Loading