In Laravel 8, I have already had a working DocumentPdf
resource – at least I was able to create the object using DocumentPdf::create($attributes)
.
Now I want to create a custom method in the model, a one that will fill the document with $data
and send it to browser. While it works for existing documents, adding the method broke create()
in a way that when I send a filled form, I receive a IlluminateDatabaseQueryException
:
SQLSTATE[HY000]: General error: 1364 Field ‘filename’ doesn’t have a default value (SQL: insert into
document_pdfs
(updated_at
,created_at
) values (2020-10-22 02:42:26, 2020-10-22 02:42:26))
It’s not a first custom method in the model, but the only breaking one – commenting out the method makes create()
work again, but then I lose an ability to fill the document.
app/DocumentPdf.php
:
namespace App; use IlluminateDatabaseEloquentModel; use mikehaertlpdftkPdf; class DocumentPdf extends Model { // (... some constants, static methods and relations ...) /** * The attributes that should be hidden for arrays. * * @var array */ protected $guarded = []; /** * Checks whether the document is active * * @return bool */ public function isActive() { return $this->active; } /** * Get full path to the file * * @return string */ public function fullpath() { return storage_path('app' . DIRECTORY_SEPARATOR . $this->path); } /** * Fill the document and send it to browser * */ public function fill(array $data) { $pdf = new Pdf($this->fullpath()); $pdf->fillForm($data) ->needAppearances() ->send(date('YmdHis') . '.pdf'); }
app/Http/Controllers/DocumentPdfController.php
:
namespace AppHttpControllers; use AppDocumentPdf; use IlluminateHttpRequest; use mikehaertlpdftkPdf; class DocumentPdfController extends Controller { // (...) /** * Show the form for creating a new resource. * * @return IlluminateHttpResponse */ public function create() { return view('documents.create'); } /** * Store a newly created resource in storage. * * @param IlluminateHttpRequest $request * @return IlluminateHttpResponse */ public function store(Request $request) { $this->authorize('create', DocumentPdf::class); DocumentPdf::create($this->validateFormAndStoreFile()); return redirect(route('documents.index')) ->with('model-message', 'Document created'); } // (...) /** * Validate and parse data for storing/updating the resource. * * @return Array */ protected function validateFormAndStoreFile() { $form = $this->validateForm(); $form += $this->storeFile(); $form += $this->discoverFields($form['path']); unset($form['file']); return $form; } /** * Validate data for storing/updating the resource. * * @return Array */ protected function validateForm() { return request()->validate([ 'display_name' => 'nullable|max:255', 'description' => 'nullable', 'file' => 'required|file|mimes:pdf', ]); } /** * Parse data for storing/updating the resource in database. * * @return Array */ protected function storeFile() { $path = request('file')->store('documents'); $form = [ 'filename' => request('file')->getClientOriginalName(), 'extension' => request('file')->extension(), 'size' => request('file')->getSize(), 'mimetype' => request('file')->getMimeType(null), 'path' => $path ]; return $form; } /** * Discover PDF file fields. * Todo: I feel like it should be handled in DocumentPdf model * but failed to do so due to BadMethodCallException - to be verified * * @return Array */ protected function discoverFields($filename) { $pdf = new Pdf(storage_path('app' . DIRECTORY_SEPARATOR . $filename)); return [ 'fields' => json_encode($pdf->getDataFields()), ]; }
How could I define such method not to break existing functionality? I’d like to have it in a model, since in the future I plan to handle other types of documents, e. g. DocumentXls
, and have a parent abstract class Document
that’d be handled by DocumentController
.
Advertisement
Answer
fill
is a Model method that fills the attributes of the Model. You should not override this; change your method name to something else.
IlluminateDatabaseEloquentBuilder@create
creates a new instance of the Model with the data passed, which will call fill
to fill the attributes for the Model.