diff --git a/app/Http/Controllers/CarController.php b/app/Http/Controllers/CarController.php
index 00a9490..7fcbc77 100644
--- a/app/Http/Controllers/CarController.php
+++ b/app/Http/Controllers/CarController.php
@@ -373,6 +373,17 @@ class CarController extends Controller
'known_damage' => $car->known_damage,
'notes' => $car->notes,
'deleted_at' => $car->deleted_at,
+ 'documents' => $car->documents()->orderBy('created_at', 'asc')->get()
+ ->map(function ($document) {
+ return [
+ 'id' => $document->id,
+ 'name' => $document->name,
+ 'size' => $document->size,
+ 'extension' => $document->extension,
+ 'link' => $document->link,
+ 'created_at' => $document->created_at,
+ ];
+ }),
'buy_contracts' => $car->buyContracts()
->orderBy('date', 'desc')
->with('contact')
diff --git a/app/Http/Controllers/ContactController.php b/app/Http/Controllers/ContactController.php
index f7eb015..4c49331 100644
--- a/app/Http/Controllers/ContactController.php
+++ b/app/Http/Controllers/ContactController.php
@@ -212,6 +212,17 @@ class ContactController extends Controller
'city' => $contact->city,
'country' => $contact->country,
'deleted_at' => $contact->deleted_at,
+ 'documents' => $contact->documents()->orderBy('created_at', 'asc')->get()
+ ->map(function ($document) {
+ return [
+ 'id' => $document->id,
+ 'name' => $document->name,
+ 'size' => $document->size,
+ 'extension' => $document->extension,
+ 'link' => $document->link,
+ 'created_at' => $document->created_at,
+ ];
+ }),
'buy_contracts' => $contact->buyContracts()
->orderBy('date', 'desc')
->with('car')
diff --git a/app/Http/Controllers/DocumentController.php b/app/Http/Controllers/DocumentController.php
index dbdddd2..e795b33 100644
--- a/app/Http/Controllers/DocumentController.php
+++ b/app/Http/Controllers/DocumentController.php
@@ -7,10 +7,11 @@ use App\Models\Document;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Redirect;
+use Illuminate\Database\Eloquent\Relations\Relation;
class DocumentController extends Controller
{
- public function show(Contract $contract, Document $document)
+ public function show(Document $document)
{
if (file_exists($document->path)) {
header('Content-Disposition: filename="' . $document->name . '"');
@@ -20,8 +21,14 @@ class DocumentController extends Controller
abort(404);
}
- public function store(Request $request, Contract $contract)
+ public function store(Request $request)
{
+ $class = $request->get('documentable_type');
+ $id = $request->get('documentable_id');
+ if (!in_array($class, ['contracts', 'cars', 'contacts'])) {
+ return [];
+ }
+
$file = $request->file()['document'];
$internalName = date('Y-m-d-H-i-s') . '.' . $file->extension();
$document = Document::create([
@@ -29,9 +36,10 @@ class DocumentController extends Controller
'internal_name' => $internalName,
'size' => $file->getSize(),
'extension' => $file->extension() ?? '',
- 'contract_id' => $contract->id,
+ 'documentable_type' => $class,
+ 'documentable_id' => $id,
]);
- $file->move(public_path("documents/contracts/{$contract->id}/"), $internalName);
+ $file->move(public_path("documents/{$class}/{$id}/"), $internalName);
return [
'id' => $document->id,
@@ -43,7 +51,7 @@ class DocumentController extends Controller
];
}
- public function destroy(Request $request, Contract $contract)
+ public function destroy(Request $request)
{
$document = Document::find((int)$request->get('id'));
diff --git a/app/Models/Car.php b/app/Models/Car.php
index 621f62e..5b90d5e 100644
--- a/app/Models/Car.php
+++ b/app/Models/Car.php
@@ -2,9 +2,8 @@
namespace App\Models;
-use App\Enums\ContractType;
use Carbon\Carbon;
-use Cknow\Money\Money;
+use App\Enums\ContractType;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -25,6 +24,11 @@ class Car extends Model
'car_model_id',
];
+ public function documents()
+ {
+ return $this->morphMany(Document::class, 'documentable');
+ }
+
public function getNameAttribute()
{
if (!$this->carModel) {
diff --git a/app/Models/Contact.php b/app/Models/Contact.php
index 693ace2..ab929d0 100644
--- a/app/Models/Contact.php
+++ b/app/Models/Contact.php
@@ -25,6 +25,11 @@ class Contact extends Model
'notes',
];
+ public function documents()
+ {
+ return $this->morphMany(Document::class, 'documentable');
+ }
+
public function getNameAttribute()
{
return implode(' ', array_filter([$this->lastname, $this->firstname]));
diff --git a/app/Models/Contract.php b/app/Models/Contract.php
index 54ebed4..5ce5968 100644
--- a/app/Models/Contract.php
+++ b/app/Models/Contract.php
@@ -96,7 +96,7 @@ class Contract extends Model
public function documents()
{
- return $this->hasMany(Document::class);
+ return $this->morphMany(Document::class, 'documentable');
}
public function payments()
diff --git a/app/Models/Document.php b/app/Models/Document.php
index 4121eb6..353de1b 100644
--- a/app/Models/Document.php
+++ b/app/Models/Document.php
@@ -15,9 +15,15 @@ class Document extends Model
'internal_name',
'extension',
'size',
- 'contract_id',
+ 'documentable_type',
+ 'documentable_id',
];
+ public function documentable()
+ {
+ return $this->morphTo();
+ }
+
public function getCreatedAtAttribute($created_at)
{
return Carbon::parse($created_at)->format('d.m.Y');
@@ -43,16 +49,11 @@ class Document extends Model
public function getLinkAttribute()
{
- return route('documents.show', [$this->contract->id, $this->id]);
+ return route('documents.show', $this->id);
}
public function getPathAttribute()
{
- return public_path("documents/contracts/{$this->contract->id}/{$this->internal_name}");
- }
-
- public function contract()
- {
- return $this->belongsTo(Contract::class)->withTrashed();
+ return public_path("documents/{$this->documentable_type}/{$this->documentable->id}/{$this->internal_name}");
}
}
diff --git a/app/Providers/MorphServiceProvider.php b/app/Providers/MorphServiceProvider.php
new file mode 100644
index 0000000..e841f91
--- /dev/null
+++ b/app/Providers/MorphServiceProvider.php
@@ -0,0 +1,32 @@
+ 'App\Models\Contract',
+ 'cars' => 'App\Models\Car',
+ 'contacts' => 'App\Models\Contact',
+ ]);
+ }
+
+ /**
+ * Register services.
+ *
+ * @return void
+ */
+ public function register()
+ {
+ //
+ }
+}
diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php
index 2c9cdcc..8cc1366 100644
--- a/app/Providers/RouteServiceProvider.php
+++ b/app/Providers/RouteServiceProvider.php
@@ -41,15 +41,6 @@ class RouteServiceProvider extends ServiceProvider
$this->configureRateLimiting();
$this->routes(function () {
- Route::prefix('api')
- ->middleware('api')
- ->namespace($this->namespace)
- ->group(base_path('routes/api.php'));
-
- Route::middleware('web')
- ->namespace($this->namespace)
- ->group(base_path('routes/web.php'));
-
Route::bind('car', function ($value) {
if (in_array(Route::currentRouteName(), ['cars.show', 'cars.restore'])) {
return Car::withTrashed()->find($value);
@@ -70,6 +61,15 @@ class RouteServiceProvider extends ServiceProvider
}
return Contract::find($value);
});
+
+ Route::prefix('api')
+ ->middleware('api')
+ ->namespace($this->namespace)
+ ->group(base_path('routes/api.php'));
+
+ Route::middleware('web')
+ ->namespace($this->namespace)
+ ->group(base_path('routes/web.php'));
});
}
diff --git a/config/app.php b/config/app.php
index 1aef992..96b6c5f 100644
--- a/config/app.php
+++ b/config/app.php
@@ -171,6 +171,7 @@ return [
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
+ App\Providers\MorphServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
@@ -229,7 +230,7 @@ return [
'URL' => Illuminate\Support\Facades\URL::class,
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,
- 'PDF' => Barryvdh\DomPDF\Facade::class,
+ 'PDF' => Barryvdh\DomPDF\Facade::class,
],
];
diff --git a/database/factories/DocumentFactory.php b/database/factories/DocumentFactory.php
index 023fe67..e2ed364 100644
--- a/database/factories/DocumentFactory.php
+++ b/database/factories/DocumentFactory.php
@@ -26,7 +26,8 @@ class DocumentFactory extends Factory
return [
'name' => 'Vertrag.pdf',
'internal_name' => '2021-06-11-13:11:12.pdf',
- 'contract_id' => $this->faker->numberBetween(1, Contract::count()),
+ 'documentable_id' => $this->faker->numberBetween(1, Contract::count()),
+ 'documentable_type' => 'contracts',
'size' => $this->faker->numberBetween(1, 30000),
'extension' => 'pdf',
];
diff --git a/database/migrations/2021_05_10_144114_create_documents_table.php b/database/migrations/2021_05_10_144114_create_documents_table.php
index bf64420..6152a75 100644
--- a/database/migrations/2021_05_10_144114_create_documents_table.php
+++ b/database/migrations/2021_05_10_144114_create_documents_table.php
@@ -19,10 +19,7 @@ class CreateDocumentsTable extends Migration
$table->string('internal_name');
$table->integer('size');
$table->string('extension');
- $table->foreignId('contract_id')
- ->onUpdate('cascade')
- ->onDelete('cascade')
- ->constrained('contracts');
+ $table->morphs('documentable');
$table->timestamps();
});
}
diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php
index e039ee4..7eb2337 100644
--- a/database/seeders/DatabaseSeeder.php
+++ b/database/seeders/DatabaseSeeder.php
@@ -73,10 +73,6 @@ class DatabaseSeeder extends Seeder
$payments = Payment::factory()
->count(60)
->create();
-
- $documents = Document::factory()
- ->count(40)
- ->create();
}
public function getBrands(): array
diff --git a/public/js/app.js b/public/js/app.js
index 2928e44..5e011dc 100644
--- a/public/js/app.js
+++ b/public/js/app.js
@@ -18486,6 +18486,7 @@ var STATUS_FAILED = 2;
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({
props: {
id: Number,
+ documentable_type: String,
documents: Object
},
data: function data() {
@@ -18517,7 +18518,8 @@ var STATUS_FAILED = 2;
// upload data to the server
this.currentStatus = STATUS_SAVING;
- axios.post(this.route('documents.store', this.id), formData).then(function (response) {
+ console.log(this.route('documents.store'));
+ axios.post(this.route('documents.store'), formData).then(function (response) {
_this.documents.push(response.data);
_this.reset();
@@ -18529,7 +18531,9 @@ var STATUS_FAILED = 2;
filesChange: function filesChange(fieldName, fileList) {
// handle file changes
var formData = new FormData();
- if (!fileList.length) return; // append the files to FormData
+ if (!fileList.length) return;
+ formData.append('documentable_type', this.documentable_type);
+ formData.append('documentable_id', this.id); // append the files to FormData
Array.from(Array(fileList.length).keys()).map(function (x) {
formData.append(fieldName, fileList[x], fileList[x].name);
@@ -18573,6 +18577,7 @@ __webpack_require__.r(__webpack_exports__);
props: {
initial_documents: Object,
id: Number,
+ documentable_type: String,
show_upload: Boolean
},
data: function data() {
@@ -20737,6 +20742,8 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _Components_Buttons_RestoreButton_vue__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @/Components/Buttons/RestoreButton.vue */ "./resources/js/Components/Buttons/RestoreButton.vue");
/* harmony import */ var _Components_Contracts_ContractTable_vue__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../Components/Contracts/ContractTable.vue */ "./resources/js/Components/Contracts/ContractTable.vue");
/* harmony import */ var _Components_SmallTitle_vue__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../Components/SmallTitle.vue */ "./resources/js/Components/SmallTitle.vue");
+/* harmony import */ var _Components_Documents_View_vue__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! @/Components/Documents/View.vue */ "./resources/js/Components/Documents/View.vue");
+
@@ -20754,7 +20761,8 @@ __webpack_require__.r(__webpack_exports__);
DeleteButton: _Components_Buttons_DeleteButton_vue__WEBPACK_IMPORTED_MODULE_4__.default,
RestoreButton: _Components_Buttons_RestoreButton_vue__WEBPACK_IMPORTED_MODULE_5__.default,
ContractTable: _Components_Contracts_ContractTable_vue__WEBPACK_IMPORTED_MODULE_6__.default,
- SmallTitle: _Components_SmallTitle_vue__WEBPACK_IMPORTED_MODULE_7__.default
+ SmallTitle: _Components_SmallTitle_vue__WEBPACK_IMPORTED_MODULE_7__.default,
+ DocumentsView: _Components_Documents_View_vue__WEBPACK_IMPORTED_MODULE_8__.default
},
props: {
car: Object
@@ -21276,6 +21284,8 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _Components_Buttons_RestoreButton_vue__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @/Components/Buttons/RestoreButton.vue */ "./resources/js/Components/Buttons/RestoreButton.vue");
/* harmony import */ var _Components_Contracts_ContractTable_vue__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../Components/Contracts/ContractTable.vue */ "./resources/js/Components/Contracts/ContractTable.vue");
/* harmony import */ var _Components_SmallTitle_vue__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../Components/SmallTitle.vue */ "./resources/js/Components/SmallTitle.vue");
+/* harmony import */ var _Components_Documents_View_vue__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! @/Components/Documents/View.vue */ "./resources/js/Components/Documents/View.vue");
+
@@ -21293,7 +21303,8 @@ __webpack_require__.r(__webpack_exports__);
DeleteButton: _Components_Buttons_DeleteButton_vue__WEBPACK_IMPORTED_MODULE_4__.default,
RestoreButton: _Components_Buttons_RestoreButton_vue__WEBPACK_IMPORTED_MODULE_5__.default,
ContractTable: _Components_Contracts_ContractTable_vue__WEBPACK_IMPORTED_MODULE_6__.default,
- SmallTitle: _Components_SmallTitle_vue__WEBPACK_IMPORTED_MODULE_7__.default
+ SmallTitle: _Components_SmallTitle_vue__WEBPACK_IMPORTED_MODULE_7__.default,
+ DocumentsView: _Components_Documents_View_vue__WEBPACK_IMPORTED_MODULE_8__.default
},
props: {
contact: Object
@@ -23546,7 +23557,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
var _component_document_upload = (0,vue__WEBPACK_IMPORTED_MODULE_0__.resolveComponent)("document-upload");
- return (0,vue__WEBPACK_IMPORTED_MODULE_0__.openBlock)(), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createBlock)(vue__WEBPACK_IMPORTED_MODULE_0__.Fragment, null, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)(_component_small_title, {
+ return (0,vue__WEBPACK_IMPORTED_MODULE_0__.openBlock)(), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createBlock)("div", null, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)(_component_small_title, {
title: "Dokumente",
"class": "mb-3"
}), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)("div", _hoisted_1, [((0,vue__WEBPACK_IMPORTED_MODULE_0__.openBlock)(true), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createBlock)(vue__WEBPACK_IMPORTED_MODULE_0__.Fragment, null, (0,vue__WEBPACK_IMPORTED_MODULE_0__.renderList)($data.documents, function (document) {
@@ -23562,12 +23573,11 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
)), $props.show_upload ? ((0,vue__WEBPACK_IMPORTED_MODULE_0__.openBlock)(), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createBlock)(_component_document_upload, {
key: 0,
id: $props.id,
+ documentable_type: $props.documentable_type,
documents: $data.documents
}, null, 8
/* PROPS */
- , ["id", "documents"])) : (0,vue__WEBPACK_IMPORTED_MODULE_0__.createCommentVNode)("v-if", true)])], 64
- /* STABLE_FRAGMENT */
- );
+ , ["id", "documentable_type", "documents"])) : (0,vue__WEBPACK_IMPORTED_MODULE_0__.createCommentVNode)("v-if", true)])]);
}
/***/ }),
@@ -28417,6 +28427,8 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
var _component_contract_table = (0,vue__WEBPACK_IMPORTED_MODULE_0__.resolveComponent)("contract-table");
+ var _component_documents_view = (0,vue__WEBPACK_IMPORTED_MODULE_0__.resolveComponent)("documents-view");
+
var _component_show_page = (0,vue__WEBPACK_IMPORTED_MODULE_0__.resolveComponent)("show-page");
return (0,vue__WEBPACK_IMPORTED_MODULE_0__.openBlock)(), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createBlock)(_component_show_page, null, {
@@ -28478,7 +28490,15 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
title: $props.car.sell_contracts.length > 1 ? $props.car.sell_contracts.length + ' Verkaufsverträge' : 'Verkaufsvertrag'
}, null, 8
/* PROPS */
- , ["contracts", "carId", "show_upload", "title"])];
+ , ["contracts", "carId", "show_upload", "title"]), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)(_component_documents_view, {
+ "class": "mt-5",
+ initial_documents: $props.car.documents,
+ id: $props.car.id,
+ documentable_type: "cars",
+ show_upload: !$props.car.deleted_at
+ }, null, 8
+ /* PROPS */
+ , ["initial_documents", "id", "show_upload"])];
}),
_: 1
/* STABLE */
@@ -29307,6 +29327,8 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
var _component_contract_table = (0,vue__WEBPACK_IMPORTED_MODULE_0__.resolveComponent)("contract-table");
+ var _component_documents_view = (0,vue__WEBPACK_IMPORTED_MODULE_0__.resolveComponent)("documents-view");
+
var _component_show_page = (0,vue__WEBPACK_IMPORTED_MODULE_0__.resolveComponent)("show-page");
return (0,vue__WEBPACK_IMPORTED_MODULE_0__.openBlock)(), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createBlock)(_component_show_page, null, {
@@ -29368,7 +29390,15 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
title: $props.contact.sell_contracts.length > 1 ? $props.contact.sell_contracts.length + ' Verkaufsverträge' : 'Verkaufsvertrag'
}, null, 8
/* PROPS */
- , ["contracts", "contactId", "show_upload", "title"])];
+ , ["contracts", "contactId", "show_upload", "title"]), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)(_component_documents_view, {
+ "class": "mt-5",
+ initial_documents: $props.contact.documents,
+ id: $props.contact.id,
+ documentable_type: "contacts",
+ show_upload: !$props.contact.deleted_at
+ }, null, 8
+ /* PROPS */
+ , ["initial_documents", "id", "show_upload"])];
}),
_: 1
/* STABLE */
@@ -30069,6 +30099,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
, ["payments", "contract"]), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)(_component_documents_view, {
initial_documents: $props.contract.documents,
id: $props.contract.id,
+ documentable_type: "contracts",
show_upload: !$props.contract.deleted_at
}, null, 8
/* PROPS */
diff --git a/resources/js/Components/Documents/Upload.vue b/resources/js/Components/Documents/Upload.vue
index f8d0b16..39ba134 100644
--- a/resources/js/Components/Documents/Upload.vue
+++ b/resources/js/Components/Documents/Upload.vue
@@ -24,6 +24,7 @@ const STATUS_INITIAL = 0; const STATUS_SAVING = 1; const
export default {
props: {
id: Number,
+ documentable_type: String,
documents: Object,
},
data() {
@@ -53,7 +54,8 @@ export default {
save(formData) {
// upload data to the server
this.currentStatus = STATUS_SAVING;
- axios.post(this.route('documents.store', this.id), formData)
+ console.log(this.route('documents.store'));
+ axios.post(this.route('documents.store'), formData)
.then((response) => {
this.documents.push(response.data);
this.reset();
@@ -69,6 +71,9 @@ export default {
if (!fileList.length) return;
+ formData.append('documentable_type', this.documentable_type);
+ formData.append('documentable_id', this.id);
+
// append the files to FormData
Array
.from(Array(fileList.length).keys())
diff --git a/resources/js/Components/Documents/View.vue b/resources/js/Components/Documents/View.vue
index 201f6f8..3808874 100644
--- a/resources/js/Components/Documents/View.vue
+++ b/resources/js/Components/Documents/View.vue
@@ -1,11 +1,13 @@
-
-
-
-
-
-
-
+