Completed
Branch job-vuejs (8df734)
by Adam
17:32
created

SubmitController   C

Complexity

Total Complexity 29

Size/Duplication

Total Lines 296
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 23

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 296
rs 5.5
c 1
b 0
f 0
wmc 29
lcom 1
cbo 23

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 1
B getIndex() 0 42 6
A postIndex() 0 15 1
A getFirm() 0 23 2
A postFirm() 0 17 2
B getPreview() 0 32 5
B save() 0 62 6
A breadcrumb() 0 9 2
A next() 0 8 2
A loadDefaultFirm() 0 12 2
1
<?php
2
3
namespace Coyote\Http\Controllers\Job;
4
5
use Coyote\Events\JobWasSaved;
6
use Coyote\Firm\Benefit;
7
use Coyote\Http\Forms\Job\FirmForm;
8
use Coyote\Http\Forms\Job\JobForm;
9
use Coyote\Http\Transformers\FirmWithBenefits;
10
use Coyote\Job;
11
use Coyote\Http\Controllers\Controller;
12
use Coyote\Repositories\Contracts\FirmRepositoryInterface as FirmRepository;
13
use Coyote\Repositories\Contracts\JobRepositoryInterface as JobRepository;
14
use Coyote\Repositories\Criteria\EagerLoading;
15
use Coyote\Services\UrlBuilder\UrlBuilder;
16
use Illuminate\Http\Request;
17
use Coyote\Services\Stream\Objects\Job as Stream_Job;
18
use Coyote\Services\Stream\Activities\Create as Stream_Create;
19
use Coyote\Services\Stream\Activities\Update as Stream_Update;
20
21
class SubmitController extends Controller
22
{
23
    /**
24
     * @var JobRepository
25
     */
26
    private $job;
27
28
    /**
29
     * @var FirmRepository
30
     */
31
    private $firm;
32
33
    /**
34
     * @param JobRepository $job
35
     * @param FirmRepository $firm
36
     */
37
    public function __construct(JobRepository $job, FirmRepository $firm)
38
    {
39
        parent::__construct();
40
41
        $this->middleware('job.revalidate', ['except' => ['postTag', 'getFirmPartial']]);
42
        $this->middleware('job.session', ['except' => ['getIndex', 'postIndex', 'postTag', 'getFirmPartial']]);
43
44
        $this->breadcrumb->push('Praca', route('job.home'));
45
46
        $this->job = $job;
47
        $this->firm = $firm;
48
    }
49
50
    /**
51
     * @param Request $request
52
     * @param int $id
53
     * @return \Illuminate\View\View
54
     */
55
    public function getIndex(Request $request, $id = null)
56
    {
57
        /** @var \Coyote\Job $job */
58
        if ($id === null && $request->session()->has(Job::class)) {
59
            // get form content from session
60
            $job = $request->session()->get(Job::class);
61
        } else {
62
            $job = $this->job->findOrNew($id);
63
            $job->setDefaultUserId($this->userId);
64
65
            // load default firm regardless of offer is private or not
66
            if (!$job->firm_id) {
67
                $firm = $this->loadDefaultFirm();
68
                $firm->is_private = $job->exists && !$job->firm_id;
69
70
                $job->firm()->associate($firm);
71
            }
72
73
            $job->load(['tags', 'locations']);
74
        }
75
76
        $this->authorize('update', $job);
77
        $this->authorize('update', $job->firm);
78
79
        $form = $this->createForm(JobForm::class, $job);
80
        $request->session()->put(Job::class, $job);
81
82
        $this->breadcrumb($job);
83
84
        $popularTags = $this->job->getPopularTags();
85
        $this->public += ['popular_tags' => $popularTags];
86
87
        return $this->view('job.submit.home', [
88
            'popular_tags'      => $popularTags,
89
            'form'              => $form,
90
            'form_errors'       => $form->errors() ? $form->errors()->toJson() : '[]',
91
            'job'               => $form->toJson(),
92
            // firm information (in order to show firm nam on the button)
93
            'firm'              => $job->firm,
94
            'tags'              => collect($form->get('tags')->getChildrenValues())->toJson()
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Coyote\Services\FormBuilder\Fields\Field as the method getChildrenValues() does only exist in the following sub-classes of Coyote\Services\FormBuilder\Fields\Field: Coyote\Services\FormBuilder\Fields\Choice, Coyote\Services\FormBuilder\Fields\Collection. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
95
        ]);
96
    }
97
98
    /**
99
     * @param Request $request
100
     * @return \Illuminate\Http\RedirectResponse
101
     */
102
    public function postIndex(Request $request)
103
    {
104
        /** @var \Coyote\Job $job */
105
        $job = $request->session()->get(Job::class);
106
107
        $form = $this->createForm(JobForm::class, $job);
108
        $form->validate();
109
110
        // only fillable columns! we don't want to set fields like "city" or "tags" because they don't exists.
111
        $job->fill($form->all());
112
113
        $request->session()->put(Job::class, $job);
114
115
        return $this->next($request, redirect()->route('job.submit.firm'));
116
    }
117
118
    /**
119
     * @param Request $request
120
     * @return \Illuminate\View\View
121
     */
122
    public function getFirm(Request $request)
123
    {
124
        /** @var \Coyote\Job $job */
125
        $job = clone $request->session()->get(Job::class);
126
127
        // get all firms assigned to user...
128
        $this->firm->pushCriteria(new EagerLoading('benefits'));
129
        $firms = fractal($this->firm->findAllBy('user_id', $job->user_id), new FirmWithBenefits())->toJson();
130
131
        $this->breadcrumb($job);
132
133
        $form = $this->createForm(FirmForm::class, $job->firm);
134
135
        return $this->view('job.submit.firm')->with([
136
            'job'               => $job,
137
            'firm'              => $form->toJson(),
138
            'firms'             => $firms,
139
            'form'              => $form,
140
            'form_errors'       => $form->errors() ? $form->errors()->toJson() : '[]',
141
            'benefits'          => $form->get('benefits')->getChildrenValues(),
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Coyote\Services\FormBuilder\Fields\Field as the method getChildrenValues() does only exist in the following sub-classes of Coyote\Services\FormBuilder\Fields\Field: Coyote\Services\FormBuilder\Fields\Choice, Coyote\Services\FormBuilder\Fields\Collection. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
142
            'default_benefits'  => Benefit::getBenefitsList(), // default benefits,
143
        ]);
144
    }
145
146
    /**
147
     * @param Request $request
148
     * @return \Illuminate\Http\RedirectResponse
149
     */
150
    public function postFirm(Request $request)
151
    {
152
        /** @var \Coyote\Job $job */
153
        $job = $request->session()->get(Job::class);
154
155
        $form = $this->createForm(FirmForm::class, $job->firm);
156
        $form->validate();
157
158
        if ($job->firm->exists) {
159
            // syncOriginalAttribute() is important if user changes firm
160
            $job->firm->syncOriginalAttribute('id');
161
        }
162
163
        $request->session()->put(Job::class, $job);
164
165
        return $this->next($request, redirect()->route('job.submit.preview'));
166
    }
167
168
    /**
169
     * @param Request $request
170
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
171
     */
172
    public function getPreview(Request $request)
173
    {
174
        /** @var \Coyote\Job $job */
175
        $job = clone $request->session()->get(Job::class);
176
177
        $this->breadcrumb($job);
178
179
        $tags = $job->tags->groupBy('pivot.priority');
0 ignored issues
show
Bug introduced by
The method groupBy cannot be called on $job->tags (of type array<integer,object<Coyote\Tag>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
180
181
        $parser = app('parser.job');
182
183
        foreach (['description', 'requirements', 'recruitment'] as $name) {
184
            if (!empty($job[$name])) {
185
                $job[$name] = $parser->parse($job[$name]);
186
            }
187
        }
188
189
        if ($job->firm->is_private) {
190
            $job->firm()->dissociate();
191
        }
192
193
        if (!empty($job->firm->description)) {
194
            $job->firm->description = $parser->parse($job->firm->description);
195
        }
196
197
        return $this->view('job.submit.preview', [
198
            'job'               => $job,
199
            'tags'              => $tags,
200
            'ratesList'         => Job::getRatesList(),
201
            'employmentList'    => Job::getEmploymentList()
202
        ]);
203
    }
204
205
    /**
206
     * @param Request $request
207
     * @return \Illuminate\Http\RedirectResponse
208
     */
209
    public function save(Request $request)
210
    {
211
        /** @var \Coyote\Job $job */
212
        $job = clone $request->session()->get(Job::class);
213
214
        $this->authorize('update', $job);
215
216
        $tags = [];
217
        if ($job->tags->count()) {
0 ignored issues
show
Bug introduced by
The method count cannot be called on $job->tags (of type array<integer,object<Coyote\Tag>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
218
            $order = 0;
219
220
            foreach ($job->tags as $tag) {
221
                $model = $tag->firstOrCreate(['name' => $tag->name]);
222
223
                $tags[$model->id] = [
224
                    'priority'  => $tag->pivot->priority ?? 0,
225
                    'order'     => ++$order
226
                ];
227
            }
228
        }
229
230
        $this->transaction(function () use (&$job, $request, $tags) {
231
            $activity = $job->id ? Stream_Update::class : Stream_Create::class;
232
233
            if ($job->firm->is_private) {
234
                $job->firm()->dissociate();
235
                // firm name is required to save firm
236
            } elseif ($job->firm->name) {
237
                // user might click on "add new firm" button in form. make sure user_id is set up.
238
                $job->firm->setDefaultUserId($this->userId);
239
240
                $this->authorize('update', $job->firm);
241
242
                // fist, we need to save firm because firm might not exist.
243
                $job->firm->save();
244
245
                // reassociate job with firm. user could change firm, that's why we have to do it again.
246
                $job->firm()->associate($job->firm);
247
248
                $job->firm->benefits()->delete();
249
                $job->firm->benefits()->saveMany($job->firm->benefits);
250
            }
251
252
            $job->save();
253
254
            $job->locations()->delete();
255
            $job->locations()->saveMany($job->locations);
256
257
            $job->tags()->sync($tags);
258
259
            event(new JobWasSaved($job));
260
261
            $parser = app('parser.job');
262
            $job->description = $parser->parse($job->description);
263
264
            stream($activity, (new Stream_Job)->map($job));
265
266
            $request->session()->forget(Job::class);
267
        });
268
269
        return redirect()->to(UrlBuilder::job($job))->with('success', 'Oferta została prawidłowo dodana.');
270
    }
271
272
    /**
273
     * @param $job
274
     */
275
    private function breadcrumb($job)
276
    {
277
        if (empty($job['id'])) {
278
            $this->breadcrumb->push('Wystaw ofertę pracy', route('job.submit'));
279
        } else {
280
            $this->breadcrumb->push($job['title'], route('job.offer', [$job['id'], $job['slug']]));
281
            $this->breadcrumb->push('Edycja oferty', route('job.submit'));
282
        }
283
    }
284
285
    /**
286
     * @param Request $request
287
     * @param \Illuminate\Http\RedirectResponse $next
288
     * @return \Illuminate\Http\RedirectResponse
289
     */
290
    private function next(Request $request, $next)
291
    {
292
        if ($request->get('done')) {
293
            return $this->save($request);
294
        }
295
296
        return $next;
297
    }
298
299
    /**
300
     * Load user's default firm
301
     *
302
     * @return \Coyote\Firm
303
     */
304
    private function loadDefaultFirm()
305
    {
306
        $firm = $this->firm->findBy('user_id', $this->userId);
307
308
        if (!$firm) {
309
            /** @var \Coyote\Firm $firm */
310
            $firm = $this->firm->newInstance();
0 ignored issues
show
Bug introduced by
The method newInstance() does not seem to exist on object<Coyote\Repositori...irmRepositoryInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
311
            $firm->setDefaultUserId($this->userId);
312
        }
313
314
        return $firm;
315
    }
316
}
317