cms
cms copied to clipboard
Call to member path() on null in front-end form.
Bug description
Important, I have seen and looked at #6431 and #7294 which mention the same error message, they do not seem related to this issue.
So, we have custom form handling for our frontend forms. A long story short, it works as follows;
- User submits the form.
- AJAX request goes out to our custom FormController
- Our FormController extends the Statamic FormController and does a
parent::submit(). - This response is checked and errors are handled or a success view is returned.
- We listen for the FormSubmitted event (since we want to handle the sending of the emails ourselves)
- We create mailables based on the Email Configuration of the form.
So, explaining out of the way, I use Assets fields in the form, since Statamic does not handle the emailing so I need to store the uploaded files on disk, as per the documentation.
This throws the aforementioned exception in the FormController, but I can't seem to figure out why or what I'm doing wrong, so any nudges in the correct direction would be greatly appreciated.
How to reproduce
I don't think it's easily reproducible, we have a heavily customised setup.
But the jist would be, prevent Statamic from sending the emails by listening for the FormSubmitted event and returning false at the end of it. Create a form with one or more Assets fields. Try to submit that through AJAX with a custom FormController doing as explained in the Bug description.
Logs
[2024-08-28 10:09:55] local.ERROR: Call to a member function path() on null {"userId":"6264c9cd-9c2a-457e-9fce-360708b5aa4d","exception":"[object] (Error(code: 0): Call to a member function path() on null at /usr/share/nginx/html/vendor/statamic/cms/src/Fieldtypes/Assets/Assets.php:143)
[stacktrace]
#0 [internal function]: Statamic\\Fieldtypes\\Assets\\Assets->Statamic\\Fieldtypes\\Assets\\{closure}(false, '\\x00Symfony\\\\Compon...')
#1 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Collections/Arr.php(605): array_map(Object(Closure), Array, Array)
#2 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Collections/Collection.php(785): Illuminate\\Support\\Arr::map(Array, Object(Closure))
#3 /usr/share/nginx/html/vendor/statamic/cms/src/Fieldtypes/Assets/Assets.php(142): Illuminate\\Support\\Collection->map(Object(Closure))
#4 /usr/share/nginx/html/vendor/statamic/cms/src/Fields/Field.php(325): Statamic\\Fieldtypes\\Assets\\Assets->process(Object(Illuminate\\Http\\UploadedFile))
#5 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Collections/HigherOrderCollectionProxy.php(65): Statamic\\Fields\\Field->process()
#6 [internal function]: Illuminate\\Support\\HigherOrderCollectionProxy->Illuminate\\Support\\{closure}(Object(Statamic\\Fields\\Field), 'resume')
#7 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Collections/Arr.php(605): array_map(Object(Closure), Array, Array)
#8 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Collections/Collection.php(785): Illuminate\\Support\\Arr::map(Array, Object(Closure))
#9 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Collections/HigherOrderCollectionProxy.php(64): Illuminate\\Support\\Collection->map(Object(Closure))
#10 /usr/share/nginx/html/vendor/statamic/cms/src/Fields/Fields.php(191): Illuminate\\Support\\HigherOrderCollectionProxy->__call('process', Array)
#11 /usr/share/nginx/html/vendor/statamic/cms/src/Http/Controllers/FormController.php(60): Statamic\\Fields\\Fields->process()
#12 /usr/share/nginx/html/app/Http/Controllers/FormController.php(45): Statamic\\Http\\Controllers\\FormController->submit(Object(Statamic\\Http\\Requests\\FrontendFormRequest), Object(Statamic\\Forms\\Form))
#13 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\\Http\\Controllers\\FormController->submitStatamicForm(Object(Illuminate\\Http\\Request), 'job_detail_form')
#14 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(43): Illuminate\\Routing\\Controller->callAction('submitStatamicF...', Array)
#15 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Routing/Route.php(260): Illuminate\\Routing\\ControllerDispatcher->dispatch(Object(Illuminate\\Routing\\Route), Object(App\\Http\\Controllers\\FormController), 'submitStatamicF...')
#16 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Routing/Route.php(206): Illuminate\\Routing\\Route->runController()
#17 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php(808): Illuminate\\Routing\\Route->run()
#18 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#19 /usr/share/nginx/html/app/Http/Middleware/CheckIfAjax.php(23): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#20 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): App\\Http\\Middleware\\CheckIfAjax->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#21 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(51): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#22 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#23 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(88): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#24 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#25 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#26 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#27 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#28 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest(Object(Illuminate\\Http\\Request), Object(Illuminate\\Session\\Store), Object(Closure))
#29 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Session\\Middleware\\StartSession->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#30 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#31 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#32 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(75): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#33 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#34 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#35 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php(807): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#36 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php(786): Illuminate\\Routing\\Router->runRouteWithinStack(Object(Illuminate\\Routing\\Route), Object(Illuminate\\Http\\Request))
#37 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php(750): Illuminate\\Routing\\Router->runRoute(Object(Illuminate\\Http\\Request), Object(Illuminate\\Routing\\Route))
#38 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php(739): Illuminate\\Routing\\Router->dispatchToRoute(Object(Illuminate\\Http\\Request))
#39 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(201): Illuminate\\Routing\\Router->dispatch(Object(Illuminate\\Http\\Request))
#40 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}(Object(Illuminate\\Http\\Request))
#41 /usr/share/nginx/html/vendor/statamic/cms/src/Http/Middleware/StopImpersonating.php(12): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#42 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Statamic\\Http\\Middleware\\StopImpersonating->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#43 /usr/share/nginx/html/vendor/statamic/cms/src/Http/Middleware/DisableFloc.php(17): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#44 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Statamic\\Http\\Middleware\\DisableFloc->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#45 /usr/share/nginx/html/vendor/statamic/cms/src/Http/Middleware/CheckMultisite.php(15): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#46 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Statamic\\Http\\Middleware\\CheckMultisite->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#47 /usr/share/nginx/html/vendor/statamic/cms/src/Http/Middleware/CheckComposerJsonScripts.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#48 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Statamic\\Http\\Middleware\\CheckComposerJsonScripts->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#49 /usr/share/nginx/html/vendor/statamic/cms/src/Http/Middleware/PoweredByHeader.php(18): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#50 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Statamic\\Http\\Middleware\\PoweredByHeader->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#51 /usr/share/nginx/html/vendor/barryvdh/laravel-debugbar/src/Middleware/InjectDebugbar.php(66): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#52 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Barryvdh\\Debugbar\\Middleware\\InjectDebugbar->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#53 /usr/share/nginx/html/app/Http/Middleware/SetDefaultLocaleForUrls.php(38): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#54 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): App\\Http\\Middleware\\SetDefaultLocaleForUrls->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#55 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#56 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#57 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#58 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#59 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(51): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#60 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#61 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#62 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\ValidatePostSize->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#63 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(110): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#64 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#65 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#66 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\HandleCors->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#67 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(57): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#68 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\TrustProxies->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#69 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#70 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(176): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#71 /usr/share/nginx/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(145): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter(Object(Illuminate\\Http\\Request))
#72 /usr/share/nginx/html/public/index.php(51): Illuminate\\Foundation\\Http\\Kernel->handle(Object(Illuminate\\Http\\Request))
#73 {main}
"}
Environment
Environment
Application Name: myapp
Laravel Version: 11.21.0
PHP Version: 8.3.0
Composer Version: 2.6.6
Environment: local
Debug Mode: ENABLED
URL: myapp.test
Maintenance Mode: OFF
Timezone: UTC
Locale: en
Cache
Config: NOT CACHED
Events: CACHED
Routes: NOT CACHED
Views: CACHED
Drivers
Broadcasting: log
Cache: file
Database: mysql
Logs: stack / daily
Mail: smtp
Queue: sync
Session: file
Statamic
Addons: 2
Sites: 2 (English, Dutch)
Stache Watcher: Disabled
Static Caching: Disabled
Version: 5.23.0 PRO
Statamic Addons
spatie/statamic-responsive-images: 5.0.0
withcandour/aardvark-seo: 5.0.0
Installation
Existing Laravel app
Additional details
Form blueprint:
tabs:
main:
display: Main
sections:
-
fields:
-
handle: first_name
field:
placeholder: first_name-placeholder
display: 'First name'
type: text
listable: true
always_save: true
validate:
- required
localizable: true
-
handle: last_name
field:
placeholder: last_name-placeholder
display: 'Last name'
type: text
listable: true
always_save: true
validate:
- required
localizable: true
-
handle: email
field:
placeholder: email-placeholder
input_type: email
display: Email
type: text
listable: true
always_save: true
validate:
- required
- email
localizable: false
-
handle: phone
field:
placeholder: phone-placeholder
input_type: tel
display: 'Phone number'
type: text
listable: true
instructions_position: below
always_save: true
validate:
- required
- 'min:10'
- 'max:20'
localizable: true
-
handle: how_did_you_find_us
field:
placeholder: how_did_you_find_us-placeholder
display: 'How did you find us?'
type: text
instructions: '(e.g. Job offer page, bus ads, advertisement..)'
instructions_position: below
always_save: true
validate:
- required
localizable: false
-
handle: resume
field:
mode: grid
container: cvs
restrict: true
max_files: 1
display: 'Upload your CV'
type: assets
listable: true
instructions_position: below
always_save: true
validate:
- required
- 'mimes:pdf,doc,docx'
instructions: 'Accepted formats: pdf, doc, docx'
folder: cvs
localizable: false
-
handle: motivation
field:
mode: grid
container: cvs
restrict: true
max_files: 1
display: 'Upload Your Motivation'
type: assets
listable: true
always_save: true
validate:
- 'mimes:pdf,doc,docx'
folder: cvs
localizable: false
-
handle: retention_period
field:
inline: true
display: 'I consent that my data may be kept up to 1 year after the application date'
type: checkboxes
instructions_position: below
always_save: true
localizable: false
-
handle: general-conditions
field:
inline: true
display: 'I’ve read myapp’s privacy statement and agree to the conditions therein.'
type: checkboxes
instructions_position: below
always_save: false
validate:
- required
localizable: false
FormController (custom):
public function submitStatamicForm(Request $request, string $formHandle)
{
// Find the Statamic form by its handle.
$form = Form::find($formHandle);
if (is_null($form)) {
return response()->json(['message' => "Form '$formHandle' not found"], Response::HTTP_NOT_FOUND);
}
// Statamic expects a FrontendFormRequest class for form submissions, so create one from request
$frontendFormRequest = FrontendFormRequest::createFrom($request);
// Submit the form through Statamics' 'submit' method.
$response = parent::submit($frontendFormRequest, $form);
// On success, update the content to include a rendered form confirmation view.
// The view will have access to the "confirmation" entry and to the values submitted in the form.
if ($response->getStatusCode() === Response::HTTP_OK) {
$responseContent = $response->getOriginalContent();
$confirmationSlug = Str::replace('_', '-', $formHandle);
$confirmation = $this->formService->getFormConfirmation($confirmationSlug, $request['locale'] ?? Locale::ENGLISH->value);
if (is_null($confirmation)) {
return response()->json(['message' => "Form confirmation '$confirmationSlug' not found"], Response::HTTP_NOT_FOUND);
}
$successView = StatamicView::make(
$confirmation->originValue('template'),
[
'form' => $responseContent,
'confirmation' => $confirmation,
]
);
$responseContent['renderedSuccessView'] = $successView->render();
$response->setContent($responseContent);
}
return $response;
}
I did try using the Files fieldtypes, and this does not throw an exception, but as we handle mailing ourselves, the proper files are not added as an attachment as the documentation states.
Additional information regarding how you end up in the custom FormController. Routing
Route::controller(FormController::class)
->middleware(['ajax']) # Returns 403 error if !$request->ajax(), otherwise continues.
->group(function () {
Route::post('/ajaxforms/{formHandle}', 'submitStatamicForm')->name('submit-statamic-form');
});
AJAX form submission
'use strict'
// Define the 'forms' namespace
if (!('forms' in window)) window['forms'] = [];
$(function() {
$('body').on('click', '.form-ajax-submit', function (e) {
e.preventDefault();
forms.disableSubmit(e); // Disables the submit button after user clicked it, not relevant for issue.
forms.ajaxSubmit(e);
});
});
/*******************************
* Methods ('forms' namespace) *
*******************************/
forms.ajaxSubmit = function (e) {
let form = $(e.target).closest('form');
let formId = form.attr('id');
let formName = form.attr('name');
let formErrorSpan = form.find('span[data-failure]');
let failureMessage = formErrorSpan.data('failure');
// clear potential previously set validation errors
// forms.clearValidationErrors(form); // For this issue you can assume no validation errors happen.
// show spinning loader
$('#envelope-icon').addClass('d-none');
$('#spinning-loader').removeClass('d-none');
let formDataForm = $('form#' + formId)[0];
let formData = new FormData(formDataForm);
// If the form contains the resume fields (file) we want to append both the resume and motivation files to the FormData
if ($('input[name="resume"]').length > 0) {
formData.append('resume', $('input[name="resume"]')[0].files[0]);
formData.append('motivation', $('input[name="motivation"]')[0].files[0]);
}
$.ajax('/ajaxforms/' + formName, {
type: 'POST',
processData: false,
contentType: false,
data: formData,
})
.done(function (response) {
let formContainer = form.closest('div');
$(formContainer).html(response.renderedSuccessView);
forms.enableSubmit(e);
})
.fail(function (response) {
if (response.status === 404) {
console.log(response.responseJSON.message);
formErrorSpan.html(failureMessage);
formErrorSpan.show();
} else {
$.each(response.responseJSON.error, function (formFieldName, validationErrorText) {
let formField = form.find('[name=' + formFieldName + ']');
let formFieldValidationSpan = form.find('span.' + formFieldName);
formFieldValidationSpan.html(validationErrorText);
formFieldValidationSpan.show();
formField.addClass('is-invalid');
});
}
forms.enableSubmit(e); // Enables the submit button again, not relevant for issue
});
Below is the code for the listener, it uses Enums to check the form handle which should not be relevant to the issue.
The handleFormSubmission is part of a Trait and just sends the Mailables to the correct location
public function handle(FormSubmitted $event)
{
if ($event->submission->form->handle() === Form::APPLICATION_FORM->value) {
// Handle form submission (send mail and store submitted data)
$this->handleFormSubmission($event, ApplicationFormInternalMailable::class, ApplicationFormExternalMailable::class);
// return false so Statamic does not send the e-mail (this also stops propagation of this event to other listeners!!).
return false;
}
// return nothing so that propagation is not stopped unless another listener picks up and handles this event
}
Has this only just started happening recently or have you just built this and it's not working?
Has this only just started happening recently or have you just built this and it's not working?
Sad to say I don't really have a proper answer for this. We utilize this setup on another website, but that website does not have any file-upload fields in the forms.
This website used to run on Statamic 3, so I am now updating it. (Yes, it was greatly neglected) I followed the guides from 3 to 4 and 4 to 5, and Control Panel, and the whole shebang seems to work fine.
The form setup used to be less advanced (just the listener, no AJAX submission, and custom form controller), and when it was it worked. properly.
But we want this website to be in line with our more advanced website, thus also utilizing the more advanced form setup.
Old listener:
public function handle(FormSubmitted $event)
{
if ($event->submission->form->handle() === Form::APPLICATION_FORM->value) {
$emailJob = new SendEmail($event->submission, Site::current(), $event->submission->form->email()[0]);
dispatch($emailJob)->onQueue('every_minute');
// return false so Statamic does not send the e-mail (this also stops propagation of this event to other listeners!!).
return false;
}
// return nothing so that propagation is not stopped unless another listener picks up and handles this event
}
If you need any other information please feel free to ask. And thanks for putting up with my shenanigans again @duncanmcclean , it's much appreciated as always!
@duncanmcclean I'll take a look if I can reproduce the error on the website where this setup is working tomorrow. That would probably narrow it down to some configuration issues on our end.
We don't usually recommend that you hijack our controllers like you have. 😄
Although, if you're able to reproduce the issue on a fresh site or provide access to the site you're having the issues on, then I can take a look.
We don't usually recommend that you hijack our controllers like you have. 😄
Although, if you're able to reproduce the issue on a fresh site or provide access to the site you're having the issues on, then I can take a look.
Hijacking is a strong word, I prefer borrowing 😄
I'll report back either tomorrow or the day after, have a good one!
We don't usually recommend that you hijack our controllers like you have. 😄
Although, if you're able to reproduce the issue on a fresh site or provide access to the site you're having the issues on, then I can take a look.
I tried on our other site, no cigar. Same issue, Then I tried on a new Statamic install, same thing.
But your hijacking comment made me wonder, what is the recommended way to take over the whole mailing, without hijacking Statamic's controllers and everything?
Is there something in the docs that I'm missing, where this is thoroughly explained, and I just missed it or something?
~~I'll try and create a Github repo with my (very very ugly) minimal reproducible example.~~
I've created a Github repo with my (very very ugly) minimal reproducible example, find it here
Can't give you access to the other site. Or well, I could but the content for that site is not in the same git repo as the project's code, so it'd be a major hassle to setup.
But your hijacking comment made me wonder, what is the recommended way to take over the whole mailing, without hijacking Statamic's controllers and everything?
I don't think we really have a recommended way to not use our native mailing, other than not configuring any emails.
(I've created a Github repo with my (very very ugly) minimal reproducible example, find it here
Thanks, I'll take a look when I get a chance.
Just looking at the code, I can get the assets to upload, but they then get deleted if you return false from the FormSubmitted listener (intentionally).
Why can't you use the normal Statamic email logic?
That makes sense I guess..
Well, we've created a collection where our Content department can create dynamic email templates in all the languages that are supported on the site. When a form is submitted, we listen for it, take the content from the correct email template for the type of form that was submitted, toss that content in a mailable, and then use our custom MailJob to send the email.
Then the contacts are subscribed to one or more lists in Mailchimp, again, based on the type of form that was submitted.
Since it's quite conditional what is supposed to happen while certain forms are submitted, we thought (and subsequently decided) that it would be better to handle the mailing ourselves so we have full control over what happens. But it seems we've shot ourselves in the foot in that regard.
But I guess this issue boils down to "You're doing it wrong"?
@duncanmcclean Does Statamic's mailing logic support queues?
This is one of the other reasons we want to 'hijack' the mailing from Statamic :)
When a form is submitted, we listen for it, take the content from the correct email template for the type of form that was submitted, toss that content in a mailable, and then use our custom MailJob to send the email.
We have a config option which'll let you use your own class for sending emails: https://github.com/statamic/cms/blob/5.x/config/forms.php#L36
You could do all of your "which email template" logic in there, as well as determining if emails should actually get sent or not.
Then the contacts are subscribed to one or more lists in Mailchimp, again, based on the type of form that was submitted.
You can continue to listen to the FormSubmitted event for handling this.
Does Statamic's mailing logic support queues?
Yes, as long as you have a queue enabled, it should send emails using the queue.
But I guess this issue boils down to "You're doing it wrong"?
Yeah, sorry! We never really intended for people to do what you're doing.