Merge branch 'develop' into trunk
This commit is contained in:
commit
83c4e4d7c4
22 changed files with 266 additions and 59 deletions
|
|
@ -180,13 +180,13 @@ class Handler extends ExceptionHandler
|
|||
): JsonResponse {
|
||||
if (Helper::isRequestForApiV2($request)) {
|
||||
return (new V2ApiResponse(
|
||||
ApiStatus::KO,
|
||||
(object)array_merge($data, ['message' => $message, 'code' => $apiCode]),
|
||||
ApiStatus::KO
|
||||
))->toResponse($request)->setStatusCode($httpCode);
|
||||
}
|
||||
return (new V4ApiResponse(
|
||||
ApiStatus::KO,
|
||||
(object)array_merge($data, ['message' => $message, 'code' => $apiCode]),
|
||||
ApiStatus::KO
|
||||
))->toResponse($request)->setStatusCode($httpCode);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class OptinController extends Controller
|
|||
$singleOptin = SingleOptin::query()
|
||||
->where('email', $validatedData['email'])
|
||||
->whereHas('brand', function ($q) use ($service) {
|
||||
$q->where('id', $service->brand->id);
|
||||
$q->whereKey($service->brand->getKey());
|
||||
})
|
||||
->firstOrNew();
|
||||
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ class AnswerController extends Controller
|
|||
*/
|
||||
public function destroy(Question $question, Answer $answer): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$answer->singleOptins()->detach();
|
||||
if ($answer->deleteOrFail()) {
|
||||
return redirect()->route('bo.questions.answers.index', $question)
|
||||
->with('success', trans(
|
||||
|
|
|
|||
|
|
@ -31,11 +31,11 @@ class SingleOptinController extends Controller
|
|||
$query = SingleOptin::query()
|
||||
->when(
|
||||
$request->filled('brand'),
|
||||
fn($q) => $q->whereHas('brand', fn($q) => $q->where('id', $request->brand))
|
||||
fn($q) => $q->whereHas('brand', fn($q) => $q->whereKey($request->brand)),
|
||||
)
|
||||
->when(
|
||||
$request->filled('answer'),
|
||||
fn($q) => $q->whereHas('answers', fn($q) => $q->where('id', $request->answer))
|
||||
fn($q) => $q->whereHas('answers', fn($q) => $q->whereKey($request->answer)),
|
||||
);
|
||||
|
||||
if ($request->filled('search')) {
|
||||
|
|
@ -57,7 +57,7 @@ class SingleOptinController extends Controller
|
|||
$questionModels = Question::getAllModelsFromBrand($request->brand);
|
||||
|
||||
if ($request->filled('question')) {
|
||||
$answerModels = Answer::getAllModelsFromQuestion($request->question);
|
||||
$answerModels = Answer::getAllAnswersFromQuestion($request->question);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -78,6 +78,7 @@ class SingleOptinController extends Controller
|
|||
*/
|
||||
public function destroy(SingleOptin $singleoptin): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$singleoptin->answers()->detach();
|
||||
if ($singleoptin->deleteOrFail()) {
|
||||
return redirect()->route('bo.single_optins.index')
|
||||
->with('success', trans(
|
||||
|
|
|
|||
|
|
@ -57,9 +57,8 @@ class ApiResponse
|
|||
$status = intval($response->getStatusCode() / 100) == 2 ? ApiStatus::OK : ApiStatus::KO;
|
||||
|
||||
if (Helper::isRequestForApiV2($request)) {
|
||||
return $response->setData(new V2ApiResponse((object)$data, $status));
|
||||
return $response->setData(new V2ApiResponse($status, (object)$data));
|
||||
}
|
||||
|
||||
return $response->setData(new V4ApiResponse((object)$data, $status));
|
||||
return $response->setData(new V4ApiResponse($status, (object)$data));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class Authenticate extends Middleware
|
|||
}
|
||||
}
|
||||
}
|
||||
return route('login');
|
||||
return route('bo.login');
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -3,9 +3,12 @@
|
|||
namespace App\Http\Requests\Bo\Brands;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Uwil\LaravelCommon\Traits\Requests\HasPicture;
|
||||
|
||||
class StoreBrandRequest extends FormRequest
|
||||
{
|
||||
use HasPicture;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
|
|
@ -16,6 +19,16 @@ class StoreBrandRequest extends FormRequest
|
|||
return auth('backend')->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
$this->mergePicture('logo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
|
|
@ -23,9 +36,16 @@ class StoreBrandRequest extends FormRequest
|
|||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|min:5|max:255',
|
||||
$rules = [
|
||||
'name' => 'required|unique:brands,name|string|min:5|max:255',
|
||||
'url' => 'required|url|min:5|max:255',
|
||||
'url_claim' => 'required|url|min:5|max:255',
|
||||
'domain' => 'required|unique:brands,domain|string|min:5|max:255',
|
||||
];
|
||||
return \array_merge(
|
||||
$rules,
|
||||
$this->pictureRules('logo', 550, 550, 550, 550),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -36,7 +56,11 @@ class StoreBrandRequest extends FormRequest
|
|||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'name' => trans('Nom de la marque'),
|
||||
'name' => trans('validation.attributes.name'),
|
||||
'logo' => trans('validation.custom.logo'),
|
||||
'url' => trans('validation.custom.url'),
|
||||
'url_claim' => trans('validation.custom.url_claim'),
|
||||
'domain' => trans('validation.custom.domain'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,4 +4,34 @@ namespace App\Http\Requests\Bo\Brands;
|
|||
|
||||
class UpdateBrandRequest extends StoreBrandRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
/** @var integer $brandModelKey */
|
||||
$brandModelKey = request()->route()->parameter('brand')->getKey();
|
||||
$rules = [
|
||||
'name' => [
|
||||
'required',
|
||||
"unique:brands,name,$brandModelKey",
|
||||
'string',
|
||||
'min:5',
|
||||
'max:255',
|
||||
],
|
||||
'domain' => [
|
||||
'required',
|
||||
"unique:brands,domain,$brandModelKey",
|
||||
'string',
|
||||
'min:5',
|
||||
'max:255',
|
||||
],
|
||||
];
|
||||
return \array_merge(
|
||||
parent::rules(),
|
||||
$rules,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ class ApiResponse extends JsonResource
|
|||
protected $status;
|
||||
|
||||
/**
|
||||
* @param \stdClass|null $data
|
||||
* @param \App\Enums\ApiStatus $status
|
||||
* @param \stdClass|null $data
|
||||
*/
|
||||
public function __construct(stdClass|null $data = null, ApiStatus $status)
|
||||
public function __construct(ApiStatus $status, stdClass|null $data = null)
|
||||
{
|
||||
$this->status = $status;
|
||||
$this->data = $data;
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ class ApiResponse extends JsonResource
|
|||
protected $status;
|
||||
|
||||
/**
|
||||
* @param \stdClass|null $data
|
||||
* @param \App\Enums\ApiStatus $status
|
||||
* @param \stdClass|null $data
|
||||
*/
|
||||
public function __construct(stdClass|null $data = null, ApiStatus $status)
|
||||
public function __construct(ApiStatus $status, stdClass|null $data = null)
|
||||
{
|
||||
$this->status = $status;
|
||||
$this->data = $data;
|
||||
|
|
@ -36,8 +36,8 @@ class ApiResponse extends JsonResource
|
|||
{
|
||||
//phpcs:enable
|
||||
return [
|
||||
'status' => $this->status->value(),
|
||||
'data' => (array)($this->data ?? []),
|
||||
'status' => $this->status->value(),
|
||||
'data' => (array)($this->data ?? []),
|
||||
'version' => config('api.version')
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,19 +49,6 @@ class Answer extends Model
|
|||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Get all answers associated to the band.
|
||||
*
|
||||
* @param integer $questionModelKey
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public static function getAllModelsFromQuestion(int $questionModelKey): \Illuminate\Database\Eloquent\Collection
|
||||
{
|
||||
return self::getAnswers()->filter(
|
||||
fn($model) => $model->question->getKey() === $questionModelKey
|
||||
)->values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all cache elements related to this model.
|
||||
*
|
||||
|
|
@ -72,18 +59,22 @@ class Answer extends Model
|
|||
{
|
||||
$questionModelKey = $model->question->getKey();
|
||||
Cache::forget("question-{$questionModelKey}-answers");
|
||||
self::getAnswers();
|
||||
self::getAllAnswersFromQuestion($questionModelKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all answers.
|
||||
*
|
||||
* @param integer $questionModelKey
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public static function getAnswers(): \Illuminate\Database\Eloquent\Collection
|
||||
public static function getAllAnswersFromQuestion(int $questionModelKey): \Illuminate\Database\Eloquent\Collection
|
||||
{
|
||||
return Cache::remember('answers', 86400, function () {
|
||||
return self::query()->orderBy('answer', 'ASC')->get();
|
||||
return Cache::remember("question-{$questionModelKey}-answers", 86400, function () use ($questionModelKey) {
|
||||
return self::query()
|
||||
->whereHas('question', fn($q) => $q->whereKey($questionModelKey))
|
||||
->orderBy('answer', 'ASC')
|
||||
->get();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use App\Traits\Models\ActivityLog;
|
|||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Uwil\LaravelCommon\Helpers\FileHelper;
|
||||
|
||||
/**
|
||||
* @property integer $id Brand id.
|
||||
|
|
@ -54,14 +55,22 @@ class Brand extends Model
|
|||
*/
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::creating(function (self $model) {
|
||||
$model->logo = FileHelper::storeFile($model, $model->logo, true);
|
||||
});
|
||||
static::created(function () {
|
||||
self::resetCache();
|
||||
});
|
||||
static::updated(function () {
|
||||
self::resetCache();
|
||||
static::updating(function (self $model) {
|
||||
$model->logo = FileHelper::storeFile($model, $model->logo, true);
|
||||
});
|
||||
static::deleted(function () {
|
||||
static::updated(function (self $model) {
|
||||
self::resetCache();
|
||||
FileHelper::removeOldFile($model, 'logo');
|
||||
});
|
||||
static::deleted(function (self $model) {
|
||||
self::resetCache();
|
||||
FileHelper::removeOldFile($model, 'logo');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ class BrandFactory extends Factory
|
|||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => $this->faker->company,
|
||||
'name' => $this->faker->unique()->company,
|
||||
'url' => $this->faker->url,
|
||||
'url_claim' => $this->faker->url,
|
||||
'domain' => $this->faker->domainName,
|
||||
'domain' => $this->faker->unique()->domainName,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Brand;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Uwil\LaravelCommon\Helpers\FileHelper;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
// Set default value.
|
||||
Brand::query()->get()->map(function (Brand $brandModel) {
|
||||
$brandModel->logo = FileHelper::storeFile(
|
||||
$brandModel,
|
||||
new \SplFileInfo(
|
||||
\resource_path('../database/factories/assets/brands/default-logo-550x550.jpg')
|
||||
)
|
||||
);
|
||||
$urlExample = "https://example-{$brandModel->getKey()}.com";
|
||||
$brandModel->url = $urlExample;
|
||||
$brandModel->url_claim = $urlExample;
|
||||
$brandModel->domain = parse_url($urlExample, PHP_URL_HOST);
|
||||
$brandModel->saveOrFail();
|
||||
});
|
||||
|
||||
// Set unique keys.
|
||||
Schema::table('brands', function (Blueprint $table) {
|
||||
$table->unique('name', 'brands_name_unique');
|
||||
$table->unique('domain', 'brands_domain_unique');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('brands', function (Blueprint $table) {
|
||||
$table->dropUnique('brands_name_unique');
|
||||
$table->dropUnique('brands_domain_unique');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -233,5 +233,9 @@ return [
|
|||
'type' => 'type',
|
||||
'status' => 'status',
|
||||
'label' => 'label',
|
||||
'domain' => 'domain',
|
||||
'logo' => 'logo',
|
||||
'url' => 'url',
|
||||
'url_claim' => 'claim url',
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -233,5 +233,9 @@ return [
|
|||
'type' => 'type',
|
||||
'status' => 'status',
|
||||
'label' => 'label',
|
||||
'domain' => 'domaine',
|
||||
'logo' => 'logo',
|
||||
'url' => 'lien',
|
||||
'url_claim' => 'lien réclamation',
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{{ route('bo.brands.store') }}" method="POST">
|
||||
<form action="{{ route('bo.brands.store') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
@include('bo.pages.brands.form')
|
||||
<div class="row">
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{{ route('bo.brands.update', $brandModel->id) }}" method="POST">
|
||||
<form action="{{ route('bo.brands.update', $brandModel->id) }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
@include('bo.pages.brands.form')
|
||||
|
|
|
|||
|
|
@ -2,22 +2,111 @@
|
|||
{{-- TITLE --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="h-100 border p-3">
|
||||
<legend>{{ __('Nom') }}</legend>
|
||||
<legend>{{ str(__('validation.attributes.name'))->ucfirst() }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
@include('bo.partials.inputs.varchar', [
|
||||
'id' => 'brandName',
|
||||
'label' => __('Nom de la :model', ['model' => Str::singular(__('models.classes.brands'))]),
|
||||
'label' => __('Nom de la :model', ['model' => str(__('models.classes.brands'))->singular()]),
|
||||
'hasWordCounter' => true,
|
||||
'name' => 'name',
|
||||
'required' => true,
|
||||
'model' => $brandModel ?? null,
|
||||
'helper' => __('Doit décrire précisément la :model (3 caractères minimum, 255 maximum).', [
|
||||
'model' => str(__('models.classes.brands'))->singular(),
|
||||
]),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{-- DOMAIN --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="h-100 border p-3">
|
||||
<legend>{{ str(__('validation.custom.domain'))->ucfirst() }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
@include('bo.partials.inputs.varchar', [
|
||||
'id' => 'brandDomain',
|
||||
'label' => __(':field de la :model', [
|
||||
'field' => str(__('validation.custom.domain'))->ucfirst(),
|
||||
'model' => str(__('models.classes.brands'))->singular(),
|
||||
]),
|
||||
'hasWordCounter' => true,
|
||||
'name' => 'domain',
|
||||
'required' => true,
|
||||
'model' => $brandModel ?? null,
|
||||
'placeholder' => parse_url('https://example.com/', PHP_URL_HOST),
|
||||
'helper' => __('Doit décrire précisément le produit (3 caractères minimum, 255 maximum).'),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{-- URL --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="h-100 border p-3">
|
||||
<legend>{{ str(__('validation.custom.url'))->ucfirst() }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
@include('bo.partials.inputs.varchar', [
|
||||
'id' => 'brandUrl',
|
||||
'label' => __(':field de la :model', [
|
||||
'field' => str(__('validation.custom.url'))->ucfirst(),
|
||||
'model' => str(__('models.classes.brands'))->singular(),
|
||||
]),
|
||||
'hasWordCounter' => true,
|
||||
'name' => 'url',
|
||||
'required' => true,
|
||||
'model' => $brandModel ?? null,
|
||||
'placeholder' => 'https://example.com/',
|
||||
'helper' => __('Doit décrire précisément le produit (3 caractères minimum, 255 maximum).'),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{-- URL_CLAIM --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="h-100 border p-3">
|
||||
<legend>{{ str(__('validation.custom.url_claim'))->ucfirst() }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
@include('bo.partials.inputs.varchar', [
|
||||
'id' => 'brandUrlClaim',
|
||||
'label' => __(':field de la :model', [
|
||||
'field' => str(__('validation.custom.url_claim'))->ucfirst(),
|
||||
'model' => str(__('models.classes.brands'))->singular(),
|
||||
]),
|
||||
'hasWordCounter' => true,
|
||||
'name' => 'url_claim',
|
||||
'required' => true,
|
||||
'model' => $brandModel ?? null,
|
||||
'placeholder' => 'https://example.com/',
|
||||
'helper' => __('Doit décrire précisément le produit (3 caractères minimum, 255 maximum).'),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="col-12 mb-3">
|
||||
<fieldset class="h-100 border p-3">
|
||||
<legend>{{ str(__('validation.custom.url_claim'))->ucfirst() }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
@include('bo.partials.inputs.varchar-picture', [
|
||||
'id' => 'brandLogo',
|
||||
'name' => 'logo',
|
||||
'width' => 550,
|
||||
'height' => 550,
|
||||
'model' => $brandModel ?? null,
|
||||
'required' => true,
|
||||
'preview' => true,
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
|
|
|
|||
|
|
@ -15,17 +15,20 @@
|
|||
--}}
|
||||
|
||||
@php
|
||||
$data = [
|
||||
'id' => $id,
|
||||
'name' => $name,
|
||||
'helper' => $helper,
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'value' => $model->{$name} ?? $value ?? '',
|
||||
'preview' => $preview ?? false,
|
||||
'browseEvent' => $browseEvent ?? false,
|
||||
'required' => $required ?? false,
|
||||
];
|
||||
$data = [
|
||||
'id' => $id,
|
||||
'name' => $name,
|
||||
'helper' => __('crud.minicrud.dimensions_must_be', [
|
||||
'widthpx' => "{$width}px",
|
||||
'heightpx' => "{$height}px",
|
||||
]),
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'value' => $model->{$name} ?? ($value ?? ''),
|
||||
'preview' => $preview ?? false,
|
||||
'browseEvent' => $browseEvent ?? false,
|
||||
'required' => $required ?? false,
|
||||
];
|
||||
@endphp
|
||||
<div class="image-input" data-json='@json($data)'></div>
|
||||
@include('modules.input-error', ['inputName' => $name, 'helper' => null])
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class SingleOptinTest extends ApiBase
|
|||
|
||||
/**
|
||||
* Test set single optin on with answer associated.
|
||||
* route: apiV2.optin
|
||||
* route: apiV4.optin
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
|
@ -57,7 +57,7 @@ class SingleOptinTest extends ApiBase
|
|||
// * Make sure a customer answer is set.
|
||||
$firstAnswer = $questions->first()->answers->first();
|
||||
|
||||
$response = $this->callApiWithToken($service, 'post', route('apiV2.optin'), [
|
||||
$response = $this->callApiWithToken($service, 'post', route('apiV4.optin'), [
|
||||
'email' => 'singleoptin@hotmail.fr',
|
||||
'opt_in' => 1,
|
||||
'answer_id' => [$firstAnswer->getkey()],
|
||||
|
|
@ -134,7 +134,7 @@ class SingleOptinTest extends ApiBase
|
|||
public function testSingleOptinShouldFailIfAnswerIdDoesntExist(): void
|
||||
{
|
||||
$service = Service::query()->firstOrFail();
|
||||
$response = $this->callApiWithToken($service, 'post', route('apiV2.optin'), [
|
||||
$response = $this->callApiWithToken($service, 'post', route('apiV4.optin'), [
|
||||
'email' => 'singleoptin@hotmail.fr',
|
||||
'opt_in' => 1,
|
||||
'answer_id' => [0],
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue