Created
May 21, 2018 09:11
-
-
Save hovsep/734471e36d7be145d38c6df389072a44 to your computer and use it in GitHub Desktop.
Example of Laravel code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Http\Controllers; | |
use App\Entities\CargoCategory; | |
use App\Entities\CargoState; | |
use App\Entities\DocumentType; | |
use App\Entities\PaymentTerm; | |
use App\Entities\LocationType; | |
use App\Entities\TruckFeature; | |
use App\Entities\TruckType; | |
use App\Helpers\GoogleMap; | |
use App\Jobs\NotifyCarriersAboutNewLoad; | |
use App\Models\Cargo; | |
use App\Models\Document; | |
use App\Models\Location; | |
use App\Models\SearchFilter; | |
use Illuminate\Http\Request; | |
use Illuminate\Support\Facades\Auth; | |
use Illuminate\Support\Facades\DB; | |
use Illuminate\Support\Facades\Log; | |
use Illuminate\Support\Facades\Redirect; | |
use Illuminate\Support\Facades\Session; | |
class CargoController extends Controller | |
{ | |
/** | |
* @param Request $request | |
* @return \Illuminate\Http\JsonResponse | |
*/ | |
public function store(Request $request) | |
{ | |
$this->authorize('create', Cargo::class); | |
$this->validate($request, [ | |
'from_districts' => 'required|array', | |
'from_districts.*' => 'string|exists:districts,summ', | |
'to_districts' => 'required|array', | |
'to_districts.*' => 'string|exists:districts,summ', | |
'pickup_range' => 'required|string', | |
'delivery_range' => 'required|string', | |
'category' => 'required|string|in:' . CargoCategory::all()->implode(','), | |
'weight' => 'required|numeric|min:0', | |
'volume' => 'required|numeric|min:0', | |
'length' => 'required|numeric|min:0', | |
'width' => 'required|numeric|min:0', | |
'height' => 'required|numeric|min:0', | |
'required_truck_types' => 'required|array', | |
'required_truck_types.*' => 'string|in:' . TruckType::all()->implode(','), | |
'required_truck_features' => 'nullable|array', | |
'required_truck_features.*' => 'string|in:' . TruckFeature::all()->implode(','), | |
'description' => 'nullable|string', | |
'payment_term' => 'required|string|in:' . PaymentTerm::all()->implode(','), | |
'auction_enabled' => 'sometimes|bool', | |
'budget' => 'sometimes|required_without:auction_enabled|numeric|min:0', | |
'photos' => 'sometimes|array', | |
'photos.*' => 'file|mimes:jpeg,gif,png' | |
]); | |
list($pickupStartDate, $pickupEndDate) = explode(' - ', $request->pickup_range); | |
list($deliveryStartDate, $deliveryEndDate) = explode(' - ', $request->delivery_range); | |
try { | |
$cargo = new Cargo($request->all()); | |
if (Auth::user()->is_employee) { | |
$cargo->owner_id = Auth::user()->employer->id; | |
} else { | |
$cargo->owner_id = Auth::id(); | |
} | |
$cargo->pickup_from = $pickupStartDate; | |
$cargo->pickup_to = $pickupEndDate; | |
$cargo->delivery_from = $deliveryStartDate; | |
$cargo->delivery_to = $deliveryEndDate; | |
$cargo->auction_enabled = !($cargo->budget > 0); | |
DB::transaction(function() use ($cargo, $request) { | |
$cargo->save(); | |
if ($request->files->has('photos')) { | |
foreach ($request->files->get('photos') as $key => $item) { | |
$upload = $request->photos[$key]; | |
$document = new Document([ | |
'document_type' => DocumentType::CARGO_PHOTO, | |
'name' => $upload->getClientOriginalName(), | |
'filename' => $upload->storeAs('cargo', "cargo-{$cargo->id}" . DIRECTORY_SEPARATOR . uniqid() . '.' . $upload->extension(), 'public'), | |
]); | |
$cargo->photos()->save($document); | |
} | |
} | |
foreach($request->from_districts as $fromDistrictId) { | |
$location = new Location([ | |
'district_hash_sum' => $fromDistrictId, | |
'type' => LocationType::FROM | |
]); | |
$cargo->locations()->save($location); | |
} | |
unset($location); | |
foreach($request->to_districts as $toDistrictId) { | |
$location = new Location([ | |
'district_hash_sum' => $toDistrictId, | |
'type' => LocationType::TO | |
]); | |
$cargo->locations()->save($location); | |
} | |
$cargo->distance = GoogleMap::distance($cargo); | |
$cargo->save(); | |
dispatch_now(new NotifyCarriersAboutNewLoad($cargo)); | |
}); | |
} catch (\Exception $e) { | |
return response()->json(['error' => 'Failed to save cargo. Reason: ' . $e->getMessage()], 500); | |
} | |
return response()->json(['msg' => 'Cargo was posted']); | |
} | |
/** | |
* @param Cargo $cargo | |
* @param Request $request | |
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\JsonResponse|\Illuminate\View\View | |
*/ | |
public function show(Cargo $cargo, Request $request) | |
{ | |
try { | |
$cargo->load([ | |
'questions.owner.photo', | |
'owner.photo', | |
'carrier', | |
'bids.cargo', | |
'bids.owner.photo', | |
'bids.truck', | |
'bids.messages.sender.photo' | |
]); | |
//@todo: filter messages from other carriers | |
if ($request->isXmlHttpRequest()) { | |
return response()->json($cargo); | |
} else { | |
return view('cargo.page.index', ['cargo' => $cargo]); | |
} | |
} catch (\Exception $e) { | |
Log::error('Failed to show cargo', ['id' => $cargo->id, 'reason' => $e->getMessage()]); | |
if ($request->isXmlHttpRequest()) { | |
return response()->json(['error' => 'Failed to show cargo. Reason: ' . $e->getMessage()], 500); | |
} else { | |
return Redirect::back()->with('app-error', 'Something goes wrong'); | |
} | |
} | |
} | |
/** | |
* @param Cargo $cargo | |
* @param Request $request | |
* @return mixed | |
*/ | |
public function update(Cargo $cargo, Request $request) | |
{ | |
$this->authorize('update', $cargo); | |
$this->validate($request, [ | |
'state' => 'sometimes|string|in:' . CargoState::all()->implode(','), | |
'canceling_reason' => 'required_if:state,' . CargoState::CANCELED . '|string' | |
]); | |
try { | |
$cargo->fill($request->all()); | |
$cargo->save(); | |
return Redirect::back()->with('app-message', 'Cargo updated'); | |
} catch (\Exception $e) { | |
Log::warning('Failed to update cargo', ['reason' => $e->getMessage()]); | |
return Redirect::back()->with('app-error', 'Something goes wrong'); | |
} | |
} | |
/** | |
* @param Cargo $cargo | |
* @return mixed | |
*/ | |
public function destroy(Cargo $cargo) | |
{ | |
$this->authorize('delete', $cargo); | |
try { | |
if (!$cargo->delete()) { | |
throw new \Exception('DB error'); | |
} | |
Session::flash('app-message', 'Cargo deleted'); | |
} catch (\Exception $e) { | |
Log::error('Failed to delete cargo. Reason:' . $e->getMessage()); | |
Session::flash('app-error', 'Failed to delete cargo'); | |
} | |
return Redirect::back(); | |
} | |
public function search(Request $request) | |
{ | |
$filterParams = $request->except(['filter_name', 'save_filter', 'filter_id']); | |
if (!Auth::guest()) { | |
//Save new search filter | |
if ($request->has('save_filter') && $request->filled('filter_name')) { | |
SearchFilter::create([ | |
'name' => $request->filter_name, | |
'params' => $filterParams, | |
'owner_id' => Auth::id() | |
]); | |
} | |
//Apply saved filter | |
if ($request->has('filter_id') && !$request->has('save_filter') && !$request->filled('filter_name')) { | |
$savedFilter = SearchFilter::find($request->filter_id); | |
if (!empty($savedFilter)) { | |
return redirect()->route('cargo.search', $savedFilter->params); | |
} | |
} | |
} | |
$cargoes = Cargo::search($filterParams); | |
return view('cargo.search.index', [ | |
'cargoes' => $cargoes, | |
'filter' => $filterParams | |
]); | |
} | |
public function getQuestions(Cargo $cargo) | |
{ | |
return response()->json($cargo->questions()->with('owner.photo')->get()); | |
} | |
public function getBids(Cargo $cargo) | |
{ | |
return response()->json($cargo->bids()->with(['owner', 'truck'])->get()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Models; | |
use App\Entities\CargoState; | |
use App\Entities\LocationType; | |
use App\User; | |
use Illuminate\Database\Eloquent\Model; | |
use Illuminate\Database\Eloquent\SoftDeletes; | |
class Cargo extends Model | |
{ | |
use SoftDeletes; | |
protected $table = 'cargoes'; | |
protected $fillable = [ | |
'pickup_from', | |
'pickup_to', | |
'delivery_from', | |
'delivery_to', | |
'category', | |
'weight', | |
'volume', | |
'length', | |
'width', | |
'height', | |
'required_truck_types', | |
'required_truck_features', | |
'description', | |
'payment_term', | |
'auction_enabled', | |
'budget', | |
'distance', | |
'owner_id', | |
'carrier_id', | |
'state', | |
'canceling_reason' | |
]; | |
protected $casts = [ | |
'required_truck_features' => 'array',//Has workaround @see getRequiredTruckFeaturesAttribute | |
'required_truck_types' => 'array' | |
]; | |
/** | |
* The attributes that should be mutated to dates. | |
* | |
* @var array | |
*/ | |
protected $dates = [ | |
'deleted_at', | |
'pickup_from', | |
'pickup_to', | |
'delivery_from', | |
'delivery_to', | |
]; | |
protected $appends = [ | |
'applies_bids', | |
'has_carrier' | |
]; | |
public function owner() | |
{ | |
return $this->belongsTo(User::class); | |
} | |
public function carrier() | |
{ | |
return $this->belongsTo(User::class); | |
} | |
public function photos() | |
{ | |
return $this->morphMany(Document::class, 'parent'); | |
} | |
public function locations() | |
{ | |
return $this->morphMany(Location::class, 'parent'); | |
} | |
public function fromLocations() | |
{ | |
return $this->locations()->where('type', LocationType::FROM); | |
} | |
public function toLocations() | |
{ | |
return $this->locations()->where('type', LocationType::TO); | |
} | |
public function getStartLocationAttribute() | |
{ | |
return $this->fromLocations()->first(); | |
} | |
public function getFinishLocationAttribute() | |
{ | |
return $this->toLocations()->latest('id')->first(); | |
} | |
public function questions() | |
{ | |
return $this->hasMany(Question::class); | |
} | |
public function bids() | |
{ | |
return $this->hasMany(Bid::class); | |
} | |
public static function search(array $filter) | |
{ | |
$results = null; | |
$query = self::with('locations')->where('state', CargoState::ACTIVE); | |
if (!empty($filter['from_district'])) { | |
$query->whereHas('fromLocations', function($q) use ($filter) { | |
$q->where('district_hash_sum', $filter['from_district']); | |
}); | |
} | |
if (!empty($filter['to_district'])) { | |
$query->whereHas('toLocations', function($q) use ($filter) { | |
$q->where('district_hash_sum', $filter['to_district']); | |
}); | |
} | |
if (!empty($filter['pickup_range'])) { | |
list($pickupStart, $pickupEnd) = explode(' - ', $filter['pickup_range']); | |
if (!empty($pickupStart)) { | |
$query->where('pickup_from', '>=', $pickupStart); | |
} | |
if (!empty($pickupEnd)) { | |
$query->where('pickup_to', '<=', $pickupEnd); | |
} | |
} | |
if (!empty($filter['delivery_range'])) { | |
list($deliveryStart, $deliveryEnd) = explode(' - ', $filter['delivery_range']); | |
if (!empty($deliveryStart)) { | |
$query->where('delivery_from', '>=', $deliveryStart); | |
} | |
if (!empty($deliveryEnd)) { | |
$query->where('delivery_to', '<=', $deliveryEnd); | |
} | |
} | |
if (!empty($filter['categories']) && is_array($filter['categories'])) { | |
$query->whereIn('category', $filter['categories']); | |
} | |
if (!empty($filter['truck_types']) && is_array($filter['truck_types'])) { | |
$query->whereRaw(sprintf('JSON_CONTAINS(required_truck_types, \'%s\' )', json_encode($filter['truck_types']))); | |
} | |
if (!empty($filter['truck_features']) && is_array($filter['truck_features'])) { | |
$query->whereRaw(sprintf('JSON_CONTAINS(required_truck_features, \'%s\' )', json_encode($filter['truck_features']))); | |
} | |
if (!empty($filter['weight'])) { | |
$query->where('weight', '<=', (int) $filter['weight']); | |
} | |
if (!empty($filter['volume'])) { | |
$query->where('volume', '<=', (int) $filter['volume']); | |
} | |
if (!empty($filter['length'])) { | |
$query->where('length', '<=', (int) $filter['length']); | |
} | |
if (!empty($filter['width'])) { | |
$query->where('width', '<=', (int) $filter['width']); | |
} | |
if (!empty($filter['height'])) { | |
$query->where('height', '<=', (int) $filter['height']); | |
} | |
if (!empty($filter['added_today'])) { | |
$query->whereDate('created_at', today()); | |
} | |
$results = $query->paginate(empty($filter['per_page']) ? 30 : $filter['per_page']); | |
return $results; | |
} | |
public function getHasCarrierAttribute() | |
{ | |
return (bool) (($this->state == CargoState::HAS_CARRIER) && (!empty($this->carrier_id))); | |
} | |
public function getAppliesBidsAttribute() | |
{ | |
return (bool) (($this->state == CargoState::ACTIVE) && empty($this->carrier_id)); | |
} | |
public function getRequiredTruckFeaturesAttribute($value) | |
{ | |
return empty($value) ? [] : json_decode($value); | |
} | |
public function review() | |
{ | |
return $this->hasMany(Review::class); | |
} | |
public static function filter(array $filter = []) | |
{ | |
$query = self::select(); | |
if (!empty($filter['ids'])) { | |
$ids = explode(',', $filter['ids']); | |
if (!empty($ids)) { | |
$query->whereIn('id', $ids); | |
} | |
} | |
if (!empty($filter['state']) && is_array($filter['state'])) { | |
$query->whereIn('state', $filter['state']); | |
} | |
if (!empty($filter['category']) && is_array($filter['category'])) { | |
$query->whereIn('category', $filter['category']); | |
} | |
return $results = $query->paginate(empty($filter['per_page']) ? 30 : $filter['per_page']); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div> | |
<p class="text-center"> | |
<a data-toggle="collapse" :href="'#' + messagesBoxId" :dusk="'bid-' + bid.id + '-expand-messages'"> | |
<template v-if="bid.messages.length > 0"> | |
{{ bid.messages.length }} messages | |
</template> | |
<template v-else> | |
New message | |
</template> | |
</a> | |
</p> | |
<div :id="messagesBoxId" class="collapse"> | |
<template v-if="bid.messages.length > 0"> | |
<ul class="chat-list p-20 bg-white"> | |
<template v-for="m in bid.messages"> | |
<li class="reverse" v-if="m.sender_id == user.id"> | |
<div class="chat-time"> | |
{{ m.created_at }} | |
</div> | |
<div class="chat-content"> | |
<h5> | |
{{ m.sender.full_name }} | |
</h5> | |
<div class="box bg-light-success"> | |
{{ m.text }} | |
</div> | |
</div> | |
<div class="chat-img" v-if="!_.isEmpty(m.sender.photo)"> | |
<img :src="m.sender.photo.url"> | |
</div> | |
</li> | |
<li v-else> | |
<div class="chat-img" v-if="!_.isEmpty(m.sender.photo)"> | |
<img :src="m.sender.photo.url"> | |
</div> | |
<div class="chat-content"> | |
<h5> | |
{{ m.sender.full_name }} | |
</h5> | |
<div class="box bg-light-info"> | |
{{ m.text }} | |
</div> | |
</div> | |
<div class="chat-time"> | |
{{ m.created_at }} | |
</div> | |
</li> | |
</template> | |
</ul> | |
</template> | |
<template v-else> | |
<p class="text-center"> | |
No messages | |
</p> | |
</template> | |
<div class="card-body b-t" v-if="showForm"> | |
<div class="row"> | |
<div class="col-12"> | |
<textarea class="form-control b-0" placeholder="Type your message here" v-model="newMessage.text" dusk="message-text"></textarea> | |
<div class="text-danger" v-if="validationErrors.text"> | |
{{ _.first(validationErrors.text) }} | |
</div> | |
<div class="text-center m-t-10"> | |
<button type="button" class="btn btn-info waves-effect waves-light" @click.prevent="submit" dusk="btn-submit-message"> | |
<i class="fa fa-paper-plane-o"></i> | |
Submit | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</template> | |
<script> | |
export default { | |
props: { | |
preloadedBid: { | |
type: Object, | |
required: true | |
}, | |
//Logged in user | |
user: { | |
type: Object, | |
required: true | |
} | |
}, | |
computed: { | |
messagesBoxId: function() { | |
return 'bid_' + this.bid.id + '_messages'; | |
}, | |
showForm: function() { | |
return ((this.user.id == this.bid.owner_id) || (this.user.id == this.bid.cargo.owner_id)); | |
} | |
}, | |
data: function() { | |
return { | |
bid: _.defaultTo(this.preloadedBid, {}), | |
newMessage:{ | |
text: '' | |
}, | |
validationErrors: {} | |
} | |
}, | |
created: function() { | |
var vm = this; | |
bus.$on([ | |
'new-message', | |
], function() { | |
vm.loadMessages(); | |
}); | |
}, | |
methods: { | |
submit: function() { | |
var vm = this; | |
axios.post('/message', { | |
text: vm.newMessage.text, | |
context_id: vm.bid.id, | |
context_type: "App\\Models\\Bid" | |
}).then(function (response) { | |
bus.$emit('new-message'); | |
vm.validationErrors = {}; | |
vm.newMessage.text = ''; | |
if (response && response.data.msg) { | |
bus.$emit('app-message', response.data.msg); | |
} | |
}).catch(function (error) { | |
if (error.response && (error.response.status == 500) && error.response.data.error) { | |
bus.$emit('app-error', error.response.data.error); | |
} | |
if (error.response && (error.response.status == 422)) { | |
Vue.set(vm, 'validationErrors', error.response.data.errors); | |
} | |
}); | |
}, | |
loadMessages: function() { | |
var vm = this; | |
axios.get('/bid/' + vm.bid.id + '/messages') | |
.then(function(response) { | |
if (!_.isEmpty(response.data)) { | |
Vue.set(vm.bid, 'messages', response.data); | |
} | |
}) | |
.catch(function(error) { | |
bus.$emit('app-error', 'Failed to load messages'); | |
}); | |
} | |
} | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment