Add attachments, more pages
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
d086b29bcc
commit
a1ac81a73a
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Book;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BookController extends Controller {
|
||||
|
@ -40,7 +41,13 @@ class BookController extends Controller {
|
|||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id) {
|
||||
//
|
||||
$book = Book::find($id);
|
||||
|
||||
if (!$book) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
return view('books.show', [ 'book' => $book ]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -71,6 +78,16 @@ class BookController extends Controller {
|
|||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy($id) {
|
||||
//
|
||||
$book = Book::find($id);
|
||||
|
||||
if (!$book) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$book->delete();
|
||||
|
||||
return response()->json([
|
||||
'success' => true
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ class LocationController extends Controller {
|
|||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index() {
|
||||
//
|
||||
return view('locations.index', [ 'locations' => Location::paginate(15) ]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,7 +47,7 @@ class LocationController extends Controller {
|
|||
abort(404);
|
||||
}
|
||||
|
||||
return view('location', [ 'location' => $location ]);
|
||||
return view('locations.show', [ 'location' => $location, 'rows' => $location->books()->paginate(15) ]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,20 +2,31 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Book;
|
||||
use App\Services\BookInformation\BookLookupService;
|
||||
use Cache;
|
||||
use App\Services\BookInformation\GoogleBooks;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
||||
class LookupController {
|
||||
public function lookup($isbn) {
|
||||
if (!preg_match('/^(\d+)$/', $isbn)) {
|
||||
throw new HttpException(400);
|
||||
public function lookup(BookLookupService $service, $isbn) {
|
||||
$result = $service->lookup($isbn);
|
||||
|
||||
$arr = [
|
||||
'success' => false
|
||||
];
|
||||
|
||||
if ($result) {
|
||||
$arr = array_merge($arr, [
|
||||
'success' => true,
|
||||
'data' => $result
|
||||
]);
|
||||
}
|
||||
|
||||
return Cache::remember('isbn_' . $isbn, 86400, function() use ($isbn) {
|
||||
$google_books = new GoogleBooks();
|
||||
$book = Book::where('barcode', $isbn)->first();
|
||||
|
||||
return $google_books->lookup($isbn);
|
||||
});
|
||||
if ($book) {
|
||||
$arr['warning'] = 'Item already exists.<br />Location: ' . $book->location->name;
|
||||
}
|
||||
|
||||
return response()->json($arr);
|
||||
}
|
||||
}
|
|
@ -5,6 +5,9 @@ namespace App\Http\Controllers;
|
|||
use App\Models\Author;
|
||||
use App\Models\Book;
|
||||
use App\Models\Location;
|
||||
use App\Services\BookInformation\BookLookupService;
|
||||
use App\Services\BookInformation\GoogleBooks;
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
|
@ -16,24 +19,21 @@ class MainController extends Controller {
|
|||
}
|
||||
|
||||
public function search(Request $request) {
|
||||
$this->validate($request, [
|
||||
'query' => [ 'required' ]
|
||||
]);
|
||||
|
||||
$rows = Book::where('name', 'LIKE', '%' . $request->get('query') . '%')->paginate(15);
|
||||
$rows = Book::search($request->get('query'))->paginate(15);
|
||||
|
||||
return view('index', [ 'rows' => $rows ]);
|
||||
}
|
||||
|
||||
public function add(Request $request) {
|
||||
return view('add', [
|
||||
'locations' => Location::get(),
|
||||
'old' => array_filter($request->old('books', []), function($item) {
|
||||
return !empty($item['barcode']);
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
public function save(Request $request) {
|
||||
public function save(Request $request, BookLookupService $service) {
|
||||
$this->validate($request, [
|
||||
'location' => [ 'required' ]
|
||||
]);
|
||||
|
@ -69,6 +69,39 @@ class MainController extends Controller {
|
|||
$authors = array_map(function($author) { return $author->id; }, $authors);
|
||||
|
||||
$book->authors()->attach($authors);
|
||||
|
||||
// Lookup info from cache
|
||||
$res = $service->lookup($item['barcode']);
|
||||
|
||||
if (!empty($res)) {
|
||||
if ($thumbnail = data_get('images.thumbnail', $res)) {
|
||||
$file = $this->downloadFile($thumbnail);
|
||||
|
||||
$book->attach('thumbnail', $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a file and return the local temp path.
|
||||
*
|
||||
* @param $url
|
||||
* @return string|null
|
||||
*/
|
||||
private function downloadFile($url) {
|
||||
$client = new Client();
|
||||
|
||||
$path = tempnam(storage_path('app/temp'), 'image');
|
||||
|
||||
$res = $client->get($url, [
|
||||
'sink' => $path
|
||||
]);
|
||||
|
||||
if ($res->getStatusCode() != 200) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,12 +3,13 @@
|
|||
namespace App\Models;
|
||||
|
||||
use App\Services\Search\BookConfigurator;
|
||||
use Bnb\Laravel\Attachments\HasAttachment;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use ScoutElastic\Searchable;
|
||||
|
||||
class Book extends Model {
|
||||
|
||||
use Searchable;
|
||||
use Searchable, HasAttachment;
|
||||
|
||||
protected $indexConfigurator = BookConfigurator::class;
|
||||
|
||||
|
|
|
@ -14,4 +14,8 @@ class Location extends Model {
|
|||
protected $fillable = [
|
||||
'name',
|
||||
];
|
||||
|
||||
public function books() {
|
||||
return $this->hasMany(Book::class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Services\BookInformation\BookLookupService;
|
||||
use App\Services\BookInformation\CachedService;
|
||||
use App\Services\BookInformation\GoogleBooks;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
use Schema;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
@ -23,6 +26,8 @@ class AppServiceProvider extends ServiceProvider {
|
|||
* @return void
|
||||
*/
|
||||
public function register() {
|
||||
//
|
||||
$this->app->singleton(BookLookupService::class, function() {
|
||||
return new CachedService(new GoogleBooks());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\BookInformation;
|
||||
|
||||
use Cache;
|
||||
|
||||
class CachedService implements BookLookupService {
|
||||
private $service;
|
||||
|
||||
public function __construct($service) {
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
public function lookup($isbn) {
|
||||
return Cache::remember('isbn_' . $isbn, 86400, function() use ($isbn) {
|
||||
return $this->service->lookup($isbn);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -22,9 +22,10 @@ class GoogleBooks implements BookLookupService {
|
|||
*/
|
||||
$volume = Arr::first($results->getItems())->getVolumeInfo();
|
||||
|
||||
return [
|
||||
return (object) [
|
||||
'title' => $volume->getTitle(),
|
||||
'authors' => $volume->getAuthors()
|
||||
'authors' => $volume->getAuthors(),
|
||||
'images' => $volume->getImageLinks(),
|
||||
];
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
"require": {
|
||||
"php": "^7.3|^8.0",
|
||||
"babenkoivan/scout-elasticsearch-driver": "^4.2",
|
||||
"bnbwebexpertise/laravel-attachments": "^1.0",
|
||||
"fideloper/proxy": "^4.4",
|
||||
"fruitcake/laravel-cors": "^2.0",
|
||||
"google/apiclient": "^2.2",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "10a519557c1fd55683da44313588c40e",
|
||||
"content-hash": "7921640c91682ddbd453b7e9bb3f8e2c",
|
||||
"packages": [
|
||||
{
|
||||
"name": "asm89/stack-cors",
|
||||
|
@ -119,6 +119,107 @@
|
|||
],
|
||||
"time": "2020-08-13T17:57:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "bnbwebexpertise/laravel-attachments",
|
||||
"version": "1.0.22",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/bnbwebexpertise/laravel-attachments.git",
|
||||
"reference": "d3f4aa024449ad938d564aede64d05e19bfc5ff5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/bnbwebexpertise/laravel-attachments/zipball/d3f4aa024449ad938d564aede64d05e19bfc5ff5",
|
||||
"reference": "d3f4aa024449ad938d564aede64d05e19bfc5ff5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"bnbwebexpertise/php-uuid": ">=0.0.2",
|
||||
"doctrine/dbal": "~2.5",
|
||||
"illuminate/console": ">=5.5",
|
||||
"illuminate/database": ">=5.5",
|
||||
"illuminate/encryption": ">=5.5",
|
||||
"illuminate/routing": ">=5.5",
|
||||
"illuminate/support": ">=5.5",
|
||||
"nesbot/carbon": "^1.20 || ^2.0",
|
||||
"php": ">=7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/framework": ">=5.5"
|
||||
},
|
||||
"type": "package",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev",
|
||||
"dev-L5.4": "0.x-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Bnb\\Laravel\\Attachments\\AttachmentsServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Bnb\\Laravel\\Attachments\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "B&B Web Expertise",
|
||||
"email": "support@bnb.re"
|
||||
}
|
||||
],
|
||||
"description": "Attach files to your models, retrievable by key, group name or using the Eloquent relationship.",
|
||||
"time": "2020-06-24T15:19:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "bnbwebexpertise/php-uuid",
|
||||
"version": "0.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/bnbwebexpertise/php-uuid.git",
|
||||
"reference": "d60bf8054db1d062f2fc79c43af0ff499a49fbfa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/bnbwebexpertise/php-uuid/zipball/d60bf8054db1d062f2fc79c43af0ff499a49fbfa",
|
||||
"reference": "d60bf8054db1d062f2fc79c43af0ff499a49fbfa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-bcmath": "*",
|
||||
"php": ">=5.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.0"
|
||||
},
|
||||
"type": "package",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Bnb\\Uuid\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/helpers.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "B&B Web Expertise",
|
||||
"email": "support@bnb.re"
|
||||
}
|
||||
],
|
||||
"description": "UUID helpers",
|
||||
"time": "2017-11-18T13:18:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brick/math",
|
||||
"version": "0.9.1",
|
||||
|
@ -204,6 +305,299 @@
|
|||
"description": "implementation of xdg base directory specification for php",
|
||||
"time": "2019-12-04T15:06:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/cache",
|
||||
"version": "1.10.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/cache.git",
|
||||
"reference": "13e3381b25847283a91948d04640543941309727"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/cache/zipball/13e3381b25847283a91948d04640543941309727",
|
||||
"reference": "13e3381b25847283a91948d04640543941309727",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "~7.1 || ^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/common": ">2.2,<2.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"alcaeus/mongo-php-adapter": "^1.1",
|
||||
"doctrine/coding-standard": "^6.0",
|
||||
"mongodb/mongodb": "^1.1",
|
||||
"phpunit/phpunit": "^7.0",
|
||||
"predis/predis": "~1.0"
|
||||
},
|
||||
"suggest": {
|
||||
"alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.9.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Guilherme Blanco",
|
||||
"email": "guilhermeblanco@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Roman Borschel",
|
||||
"email": "roman@code-factory.org"
|
||||
},
|
||||
{
|
||||
"name": "Benjamin Eberlei",
|
||||
"email": "kontakt@beberlei.de"
|
||||
},
|
||||
{
|
||||
"name": "Jonathan Wage",
|
||||
"email": "jonwage@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Johannes Schmitt",
|
||||
"email": "schmittjoh@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.",
|
||||
"homepage": "https://www.doctrine-project.org/projects/cache.html",
|
||||
"keywords": [
|
||||
"abstraction",
|
||||
"apcu",
|
||||
"cache",
|
||||
"caching",
|
||||
"couchdb",
|
||||
"memcached",
|
||||
"php",
|
||||
"redis",
|
||||
"xcache"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://www.doctrine-project.org/sponsorship.html",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/phpdoctrine",
|
||||
"type": "patreon"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-07-07T18:54:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/dbal",
|
||||
"version": "2.12.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/dbal.git",
|
||||
"reference": "adce7a954a1c2f14f85e94aed90c8489af204086"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/dbal/zipball/adce7a954a1c2f14f85e94aed90c8489af204086",
|
||||
"reference": "adce7a954a1c2f14f85e94aed90c8489af204086",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/cache": "^1.0",
|
||||
"doctrine/event-manager": "^1.0",
|
||||
"ext-pdo": "*",
|
||||
"php": "^7.3 || ^8"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^8.1",
|
||||
"jetbrains/phpstorm-stubs": "^2019.1",
|
||||
"phpstan/phpstan": "^0.12.40",
|
||||
"phpunit/phpunit": "^9.4",
|
||||
"psalm/plugin-phpunit": "^0.10.0",
|
||||
"symfony/console": "^2.0.5|^3.0|^4.0|^5.0",
|
||||
"vimeo/psalm": "^3.17.2"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/console": "For helpful console commands such as SQL execution and import of files."
|
||||
},
|
||||
"bin": [
|
||||
"bin/doctrine-dbal"
|
||||
],
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Doctrine\\DBAL\\": "lib/Doctrine/DBAL"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Guilherme Blanco",
|
||||
"email": "guilhermeblanco@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Roman Borschel",
|
||||
"email": "roman@code-factory.org"
|
||||
},
|
||||
{
|
||||
"name": "Benjamin Eberlei",
|
||||
"email": "kontakt@beberlei.de"
|
||||
},
|
||||
{
|
||||
"name": "Jonathan Wage",
|
||||
"email": "jonwage@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.",
|
||||
"homepage": "https://www.doctrine-project.org/projects/dbal.html",
|
||||
"keywords": [
|
||||
"abstraction",
|
||||
"database",
|
||||
"db2",
|
||||
"dbal",
|
||||
"mariadb",
|
||||
"mssql",
|
||||
"mysql",
|
||||
"oci8",
|
||||
"oracle",
|
||||
"pdo",
|
||||
"pgsql",
|
||||
"postgresql",
|
||||
"queryobject",
|
||||
"sasql",
|
||||
"sql",
|
||||
"sqlanywhere",
|
||||
"sqlite",
|
||||
"sqlserver",
|
||||
"sqlsrv"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://www.doctrine-project.org/sponsorship.html",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/phpdoctrine",
|
||||
"type": "patreon"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-11-14T20:26:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/event-manager",
|
||||
"version": "1.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/event-manager.git",
|
||||
"reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/event-manager/zipball/41370af6a30faa9dc0368c4a6814d596e81aba7f",
|
||||
"reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/common": "<2.9@dev"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^6.0",
|
||||
"phpunit/phpunit": "^7.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Doctrine\\Common\\": "lib/Doctrine/Common"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Guilherme Blanco",
|
||||
"email": "guilhermeblanco@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Roman Borschel",
|
||||
"email": "roman@code-factory.org"
|
||||
},
|
||||
{
|
||||
"name": "Benjamin Eberlei",
|
||||
"email": "kontakt@beberlei.de"
|
||||
},
|
||||
{
|
||||
"name": "Jonathan Wage",
|
||||
"email": "jonwage@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Johannes Schmitt",
|
||||
"email": "schmittjoh@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Marco Pivetta",
|
||||
"email": "ocramius@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.",
|
||||
"homepage": "https://www.doctrine-project.org/projects/event-manager.html",
|
||||
"keywords": [
|
||||
"event",
|
||||
"event dispatcher",
|
||||
"event manager",
|
||||
"event system",
|
||||
"events"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://www.doctrine-project.org/sponsorship.html",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/phpdoctrine",
|
||||
"type": "patreon"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-05-29T18:28:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/inflector",
|
||||
"version": "2.0.3",
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Routes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Determine whether or not to automatically define attachments routes.
|
||||
| Used for local storage only as other storage should define their public URL.
|
||||
|
|
||||
*/
|
||||
'routes' => [
|
||||
'publish' => true,
|
||||
'prefix' => 'attachments',
|
||||
'middleware' => 'web',
|
||||
'pattern' => '/{id}/{name}',
|
||||
'shared_pattern' => '/shared/{token}',
|
||||
'dropzone' => [
|
||||
'upload_pattern' => '/dropzone',
|
||||
'delete_pattern' => '/dropzone/{id}',
|
||||
]
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Model
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Attachment model used
|
||||
|
|
||||
*/
|
||||
'attachment_model' => env('ATTACHMENTS_MODEL', \Bnb\Laravel\Attachments\Attachment::class),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Uuid
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Default attachment model uses an UUID column. You can define your own UUID
|
||||
| generator here : a global function name or a static class method in the form :
|
||||
| App\Namespace\ClassName@method
|
||||
|
|
||||
*/
|
||||
'uuid_provider' => 'uuid_v4_base36',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Behaviors
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Configurable behaviors :
|
||||
| - Concrete files can be delete when the database entry is deleted
|
||||
| - Dropzone delete can check for CSRF token match (set on upload)
|
||||
|
|
||||
*/
|
||||
'behaviors' => [
|
||||
'cascade_delete' => env('ATTACHMENTS_CASCADE_DELETE', true),
|
||||
'dropzone_check_csrf' => env('ATTACHMENTS_DROPZONE_CHECK_CSRF', true),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Declare the attachment model attributes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This allow to extend the attachment model with new columns
|
||||
| `dropzone_attributes` holds the public fields returned after a successful upload via DropzoneController
|
||||
|
|
||||
*/
|
||||
'attributes' => ['title', 'description', 'key', 'disk', 'filepath', 'group'],
|
||||
|
||||
'dropzone_attributes' => ['uuid', 'url', 'url_inline', 'filename', 'filetype', 'filesize', 'title', 'description', 'key', 'group'],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Attachment Storage Directory
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Defines the directory prefix where new attachment files are stored
|
||||
|
|
||||
*/
|
||||
'storage_directory' => [
|
||||
'prefix' => rtrim(env('ATTACHMENTS_STORAGE_DIRECTORY_PREFIX', 'attachments'), '/'),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Database configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Allows to set the database connection name for the module's models
|
||||
|
|
||||
*/
|
||||
'database' => [
|
||||
'connection' => env('ATTACHMENTS_DATABASE_CONNECTION'),
|
||||
],
|
||||
];
|
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Search Engine
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default search connection that gets used while
|
||||
| using Laravel Scout. This connection is used when syncing all models
|
||||
| to the search service. You should adjust this based on your needs.
|
||||
|
|
||||
| Supported: "algolia", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => env('SCOUT_DRIVER', 'elastic'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Index Prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify a prefix that will be applied to all search index
|
||||
| names used by Scout. This prefix may be useful if you have multiple
|
||||
| "tenants" or applications sharing the same search infrastructure.
|
||||
|
|
||||
*/
|
||||
|
||||
'prefix' => env('SCOUT_PREFIX', ''),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Queue Data Syncing
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option allows you to control if the operations that sync your data
|
||||
| with your search engines are queued. When this is set to "true" then
|
||||
| all automatic data syncing will get queued for better performance.
|
||||
|
|
||||
*/
|
||||
|
||||
'queue' => env('SCOUT_QUEUE', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Chunk Sizes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These options allow you to control the maximum chunk size when you are
|
||||
| mass importing data into the search engine. This allows you to fine
|
||||
| tune each of these chunk sizes based on the power of the servers.
|
||||
|
|
||||
*/
|
||||
|
||||
'chunk' => [
|
||||
'searchable' => 500,
|
||||
'unsearchable' => 500,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Soft Deletes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option allows to control whether to keep soft deleted records in
|
||||
| the search indexes. Maintaining soft deleted records can be useful
|
||||
| if your application still needs to search for the records later.
|
||||
|
|
||||
*/
|
||||
|
||||
'soft_delete' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Identify User
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option allows you to control whether to notify the search engine
|
||||
| of the user performing the search. This is sometimes useful if the
|
||||
| engine supports any analytics based on this application's users.
|
||||
|
|
||||
| Supported engines: "algolia"
|
||||
|
|
||||
*/
|
||||
|
||||
'identify' => env('SCOUT_IDENTIFY', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Algolia Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure your Algolia settings. Algolia is a cloud hosted
|
||||
| search engine which works great with Scout out of the box. Just plug
|
||||
| in your application ID and admin API key to get started searching.
|
||||
|
|
||||
*/
|
||||
|
||||
'algolia' => [
|
||||
'id' => env('ALGOLIA_APP_ID', ''),
|
||||
'secret' => env('ALGOLIA_SECRET', ''),
|
||||
],
|
||||
|
||||
];
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'client' => [
|
||||
'hosts' => [
|
||||
env('SCOUT_ELASTIC_HOST', 'localhost:9200'),
|
||||
],
|
||||
],
|
||||
'update_mapping' => env('SCOUT_ELASTIC_UPDATE_MAPPING', true),
|
||||
'indexer' => env('SCOUT_ELASTIC_INDEXER', 'single'),
|
||||
'document_refresh' => env('SCOUT_ELASTIC_DOCUMENT_REFRESH'),
|
||||
];
|
10
package.json
10
package.json
|
@ -12,10 +12,9 @@
|
|||
"devDependencies": {
|
||||
"@ttskch/select2-bootstrap4-theme": "^1.3.4",
|
||||
"axios": "^0.19",
|
||||
"bootbox": "^5.5.2",
|
||||
"bootstrap": "^4.0.0",
|
||||
"cross-env": "^7.0",
|
||||
"datatables.net-bs4": "^1.10.23",
|
||||
"datatables.net-buttons-bs4": "^1.6.5",
|
||||
"jquery": "^3.2",
|
||||
"laravel-mix": "^5.0.1",
|
||||
"lodash": "^4.17.19",
|
||||
|
@ -23,11 +22,6 @@
|
|||
"resolve-url-loader": "^2.3.1",
|
||||
"sass": "^1.20.1",
|
||||
"sass-loader": "^8.0.0",
|
||||
"select2": "^4.0.13",
|
||||
"vue": "^2.5.17",
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue-router": "^3.4.9"
|
||||
"select2": "^4.0.13"
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -8,18 +8,6 @@
|
|||
require('./bootstrap');
|
||||
|
||||
$(document).ready(function(e) {
|
||||
|
||||
$.fn.dataTable.render.authorValue = function(_, context, book) {
|
||||
let authors = [];
|
||||
|
||||
console.log(book);
|
||||
for (let author of book.authors) {
|
||||
authors.push(author.name);
|
||||
}
|
||||
|
||||
return authors.join(', ');
|
||||
};
|
||||
|
||||
var authorOptions = {
|
||||
placeholder: 'Authors',
|
||||
tags: true,
|
||||
|
@ -64,23 +52,35 @@ $(document).ready(function(e) {
|
|||
}
|
||||
|
||||
$.get('/lookup/' + barcodeValue, function(res) {
|
||||
$row.find('input[name*=name]').val(res.title);
|
||||
|
||||
var $authors = $row.find('.select2-author');
|
||||
|
||||
$authors.children('option').remove();
|
||||
|
||||
for (var i = 0; i < res.authors.length; i++) {
|
||||
$authors.append($('<option>', {value: res.authors[i], text: res.authors[i], selected: 'selected'}));
|
||||
if (res.warning) {
|
||||
bootbox.alert({
|
||||
title: 'Warning',
|
||||
message: res.warning,
|
||||
callback: function() {
|
||||
$('.barcode_input').filter(function() {
|
||||
return !this.value;
|
||||
}).focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$authors.trigger('change');
|
||||
if (res.success) {
|
||||
$row.find('input[name*=name]').val(res.data.title);
|
||||
|
||||
var $authors = $row.find('.select2-author');
|
||||
|
||||
$authors.children('option').remove();
|
||||
|
||||
for (var i = 0; i < res.data.authors.length; i++) {
|
||||
$authors.append($('<option>', {value: res.data.authors[i], text: res.data.authors[i], selected: 'selected'}));
|
||||
}
|
||||
|
||||
$authors.trigger('change');
|
||||
}
|
||||
}, 'json');
|
||||
|
||||
var count = emptyRowCount();
|
||||
|
||||
console.log('Empty rows:', count);
|
||||
|
||||
if (count < 1) {
|
||||
var firstIndex = 0,
|
||||
$container = $row.closest('.row-container');
|
||||
|
@ -116,6 +116,26 @@ $(document).ready(function(e) {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
$('.remove-item').click(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var $this = $(this),
|
||||
$row = $this.closest('tr');
|
||||
|
||||
bootbox.confirm('Are you sure?', function(result) {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
axios.delete($row.data('url')).then(function(res) {
|
||||
if (res.data.success) {
|
||||
|
||||
$row.remove();
|
||||
}
|
||||
});
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
function emptyRowCount() {
|
||||
|
|
|
@ -12,8 +12,7 @@ try {
|
|||
|
||||
require('bootstrap');
|
||||
require('select2');
|
||||
require('datatables.net-bs4');
|
||||
require('datatables.net-buttons-bs4');
|
||||
window.bootbox = require('bootbox');
|
||||
} catch (e) {}
|
||||
|
||||
/**
|
||||
|
@ -24,21 +23,4 @@ try {
|
|||
|
||||
window.axios = require('axios');
|
||||
|
||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||
|
||||
/**
|
||||
* Echo exposes an expressive API for subscribing to channels and listening
|
||||
* for events that are broadcast by Laravel. Echo and event broadcasting
|
||||
* allows your team to easily build robust real-time web applications.
|
||||
*/
|
||||
|
||||
// import Echo from 'laravel-echo';
|
||||
|
||||
// window.Pusher = require('pusher-js');
|
||||
|
||||
// window.Echo = new Echo({
|
||||
// broadcaster: 'pusher',
|
||||
// key: process.env.MIX_PUSHER_APP_KEY,
|
||||
// cluster: process.env.MIX_PUSHER_APP_CLUSTER,
|
||||
// forceTLS: true
|
||||
// });
|
||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
|
@ -6,6 +6,9 @@
|
|||
<div class="form-row justify-content-md-center">
|
||||
<div class="col-4">
|
||||
<select name="location" class="form-control select2-location">
|
||||
@foreach ($locations as $location)
|
||||
<option value="{{ $location->name }}">{{ $location->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -21,9 +24,11 @@
|
|||
@endif
|
||||
</div>
|
||||
<div class="row">
|
||||
<button class="btn btn-primary" name="save" type="submit">
|
||||
Save
|
||||
</button>
|
||||
<div class="col-2">
|
||||
<button class="btn btn-primary" name="save" type="submit" style="width:100%">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@include('partials/row', [ 'id' => 'add-template', 'htmlClass' => 'invisible' ])
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
@extends('layouts.main')
|
||||
|
||||
@section('content')
|
||||
<h3>{{ $book->name }}</h3>
|
||||
<h5>By: {{ $book->authors->pluck('name')->join(', ') }}</h5>
|
||||
<br />
|
||||
@if ($thumbnail = $book->attachment('thumbnail'))
|
||||
<h5>Image</h5>
|
||||
<p>
|
||||
<img src="{{ $thumbnail->url }}" />
|
||||
</p>
|
||||
@endif
|
||||
|
||||
<h5>Location:</h5>
|
||||
<p>
|
||||
{{ $book->location->name }}
|
||||
</p>
|
||||
@endsection
|
|
@ -1,11 +1,12 @@
|
|||
@extends('layouts.main')
|
||||
|
||||
@section('content')
|
||||
<table class="table table-compact">
|
||||
<table class="table table-compact table-striped">
|
||||
<thead>
|
||||
<th>Name</th>
|
||||
<th>Authors</th>
|
||||
<th>Location</th>
|
||||
<th>Actions</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($rows as $row)
|
||||
|
@ -13,6 +14,9 @@
|
|||
<td>{{ $row->name }}</td>
|
||||
<td>{{ $row->authors->pluck('name')->join(', ') }}</td>
|
||||
<td>{{ $row->location->name }}</td>
|
||||
<td>
|
||||
<a class="btn btn-info btn-sm" href="{{ route('books.show', $row->id) }}">View</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
</main><!-- /.container -->
|
||||
|
||||
<script src="{{ mix('js/app.js') }}"></script>
|
||||
<script src="{{ asset('vendor/datatables/buttons.server-side.js') }}"></script>
|
||||
@stack('scripts')
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
@extends('layouts.main')
|
||||
|
||||
@section('content')
|
||||
<table class="table table-compact">
|
||||
<thead>
|
||||
<th>Name</th>
|
||||
<th>Actions</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($locations as $row)
|
||||
<tr>
|
||||
<td>{{ $row->name }}</td>
|
||||
<td>
|
||||
<a href="{{ route('locations.show', $row->id) }}" class="btn btn-info btn-sm">View</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
{{ $locations->links() }}
|
||||
@endsection
|
|
@ -0,0 +1,24 @@
|
|||
@extends('layouts.main')
|
||||
|
||||
@section('content')
|
||||
<h3>{{ $location->name }}</h3>
|
||||
<table class="table table-compact">
|
||||
<thead>
|
||||
<th>Name</th>
|
||||
<th>Authors</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($rows as $row)
|
||||
<tr data-id="{{ $row->id }}" data-url="{{ route('books.show', $row->id) }}">
|
||||
<td>{{ $row->name }}</td>
|
||||
<td>{{ $row->authors->pluck('name')->join(', ') }}</td>
|
||||
<td>
|
||||
<a class="btn btn-info btn-sm" href="{{ route('books.show', $row->id) }}">View</a>
|
||||
<button class="remove-item btn btn-danger btn-sm">Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
{{ $rows->links() }}
|
||||
@endsection
|
|
@ -11,7 +11,10 @@
|
|||
<a class="nav-link" href="/">Home <span class="sr-only">(current)</span></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/add">Add</a>
|
||||
<a class="nav-link" href="{{ route('locations.index') }}">Locations</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ route('add') }}">Add</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="form-inline my-2 my-lg-0" action="{{ route('search') }}">
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
*/
|
||||
|
||||
Route::get('/', 'MainController@index');
|
||||
Route::get('/add', 'MainController@add');
|
||||
Route::get('/add', 'MainController@add')->name('add');
|
||||
Route::post('/save', 'MainController@save')->name('save');
|
||||
Route::get('/search', 'MainController@search')->name('search');
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
*
|
||||
!public/
|
||||
!temp/
|
||||
!.gitignore
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
*
|
||||
.gitignore
|
||||
!.gitignore
|
Loading…
Reference in New Issue