<?php
namespace App\Form;
use App\Entity\Brand;
use App\Entity\Model;
use App\Entity\Modification;
use App\Entity\Series;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints as Assert;
class ModificationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add("name", TextType::class, [
"attr" => ["class" => "form-control"],
"label" => "Наименование",
"label_attr" => [
"class" => "col-sm-2 form-control-label"
],
"constraints" => [
new Assert\NotBlank(),
new Assert\Length(["max" => 255]),
],
"required" => true,
])
->add("alt_name", TextType::class, [
"attr" => ["class" => "form-control"],
"label" => "Альтернативное наименование",
"label_attr" => [
"class" => "col-sm-2 form-control-label"
],
"constraints" => [
new Assert\NotBlank(),
new Assert\Length(["max" => 255]),
],
"required" => true,
])
->add("seo_name", TextType::class, [
"attr" => ["class" => "form-control"],
"label" => "SEO Name",
"label_attr" => [
"class" => "col-sm-2 form-control-label"
],
"constraints" => [
new Assert\NotBlank(),
new Assert\Length(["max" => 100]),
],
"required" => true,
])
->add("power_kwt", NumberType::class, [
"attr" => ["class" => "form-control"],
"label" => "Мощность кВт",
"label_attr" => [
"class" => "col-sm-2 form-control-label"
],
"constraints" => [
new Assert\NotBlank(),
new Assert\Positive(),
],
"empty_data" => 0,
"required" => true,
])
->add("power_horse", NumberType::class, [
"attr" => ["class" => "form-control"],
"label" => "Мощность л.с.",
"label_attr" => [
"class" => "col-sm-2 form-control-label"
],
"constraints" => [
new Assert\NotBlank(),
new Assert\Positive()
],
"empty_data" => 0,
"required" => true,
])
->add("year_from", ChoiceType::class, [
"attr" => ["class" => "form-control"],
"label" => "Год начала производства",
"label_attr" => [
"class" => "col-sm-2 form-control-label"
],
"choices" => $this->getYears(),
"constraints" => [
new Assert\NotBlank(),
new Assert\Length(["max" => 4]),
],
"required" => true,
])
->add("year_to", ChoiceType::class, [
"attr" => ["class" => "form-control"],
"label" => "Год завершения производства",
"label_attr" => [
"class" => "col-sm-2 form-control-label"
],
"choices" => $this->getYears(),
"empty_data" => "н.в.",
"constraints" => [
new Assert\NotBlank(),
new Assert\Length(["max" => 4]),
],
"required" => false,
])
->add("brand", EntityType::class, [
"label" => "Бренд",
"label_attr" => [
"class" => "col-sm-2 form-control-label"
],
"class" => Brand::class,
"choice_label" => function ($brand) {
return $brand->getName();
},
"attr" => [
"class" => "form-control select"
],
"constraints" => [
new Assert\NotBlank()
]
])
->add("model", ChoiceType::class, [
"label" => "Модель",
"label_attr" => [
"class" => "col-sm-2 form-control-label"
],
"choices" => [],
"attr" => [
"class" => "form-control select"
],
"constraints" => [
new Assert\NotBlank()
],
"disabled" => true
])
->add("series", ChoiceType::class, [
"label" => "Серия",
"label_attr" => [
"class" => "col-sm-2 form-control-label"
],
"choices" => [],
"attr" => [
"class" => "form-control select"
],
"constraints" => [
new Assert\NotBlank()
],
"disabled" => true
]);
$builder->get("brand")->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) {
$form = $event->getForm();
$brand_id = $event->getData();
$form->getParent()->add("model", EntityType::class, [
"label_attr" => [
"class" => "col-sm-2 form-control-label"
],
"attr" => [
"class" => "form-control select"
],
"class" => Model::class,
"query_builder" => function (EntityRepository $er) use ($brand_id) {
return $er->createQueryBuilder("m")
->andWhere("m.brand = :brand_id")
->setParameter("brand_id", $brand_id)
->orderBy("m.name", "ASC");
},
"choice_label" => function ($entity)
{
return $entity->getName();
},
"constraints" => [
new Assert\NotBlank()
],
"required" => true
]);
$form->getParent()->add("series", EntityType::class, [
"label_attr" => [
"class" => "col-sm-2 form-control-label"
],
"attr" => [
"class" => "form-control select"
],
"class" => Series::class,
"query_builder" => function (EntityRepository $er) use ($brand_id) {
return $er->createQueryBuilder("s")
->andWhere("s.brand = :brand_id")
->setParameter("brand_id", $brand_id)
->orderBy("s.name", "ASC");
},
"choice_label" => function ($entity)
{
return $entity->getName();
},
"constraints" => [
new Assert\NotBlank()
],
"required" => true
]);
}
);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
"data_class" => Modification::class,
]);
}
/**
* @return array
*/
private function getYears(): array
{
$years = [];
for ($i = date("Y"); $i >= 1955; $i--) {
$years[$i] = $i;
}
return $years;
}
}
import { app } from '@/main'
import { Cookie } from '@/plugins/cookie'
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import Vue from 'vue'
import { sleep } from '@/Utils'
// Full config: https://github.com/axios/axios#request-config
// axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || '';
// axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
/* eslint-disable */
const config = {
baseURL: process.env.VUE_APP_API,
timeout: 30000,
validateStatus (status: number) {
return status < 500 // Resolve only if the status code is less than 500
}
}
/* eslint-enable */
const _axios: AxiosInstance = axios.create(config)
const cookie: Cookie = new Cookie()
let isRefreshTokenProcess = false
const promises: any[] = []
/* eslint-disable */
// @ts-ignore
_axios.interceptors.request.use(async (config: AxiosRequestConfig): AxiosRequestConfig | Promise<AxiosRequestConfig> => {
// todo: set locale optional
config.headers.Language = 'ru'
if (isRefreshTokenProcess) {
console.log('%c%s', 'color: red;', `Запрос ${config.url} ожидает обновление токена...`)
promises.push(new Promise(async (resolve) => {
while (isRefreshTokenProcess) {
await sleep(500)
}
console.log('%c%s', 'color: green;', `Запрос ${config.url} разрешён!`)
resolve()
}))
// This is process update token
await Promise.all(promises)
}
if (cookie.has('access_token')) {
config.headers.Authorization = `Bearer ${cookie.get('access_token')}`
return config
} else {
if (cookie.has('refresh_token')) {
console.log('%c%s', 'color: blue;', 'Обновление токена...')
isRefreshTokenProcess = true
await axios.post(`${process.env.VUE_APP_API}/account/authorization/refresh-token`, {
refresh_token: cookie.get('refresh_token')
}).then((response: AxiosResponse) => {
if (response.status === 200) {
cookie.set('access_token', response.data.access_token, { 'max-age': 600, 'path': '/' })
cookie.set('refresh_token', response.data.refresh_token, { path: '/' })
config.headers.Authorization = `Bearer ${cookie.get('access_token')}`
}
}).catch(() => {
app.$router.replace({ name: 'login' }).then()
})
}
isRefreshTokenProcess = false
return Promise.resolve(config)
}
},
function (error) {
// Do something with request error
return Promise.reject(error)
}
)
/* eslint-disable */
/*
Array of patterns to ignore status checks.
*/
// Add a response interceptor
_axios.interceptors.response.use(
(response): Promise<AxiosResponse> | any => {
if (response.status === 401) {
return app.$router.replace({ name: 'login' }).finally(() => Promise.reject(response))
} else {
return response
}
},
function (error) {
// Do something with response error
return Promise.reject(error)
}
)
class AxiosPlugin {
public install () {
Object.defineProperties(Vue.prototype, {
axios: {
get () {
return _axios
}
},
$axios: {
get () {
return _axios
}
}
})
}
}
const axiosPlugin: AxiosPlugin = new AxiosPlugin()
Vue.use(axiosPlugin)
export default axiosPlugin
export const $axios: AxiosInstance = _axios
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// вызывается до подтверждения пути, соответствующего этому компоненту.
// НЕ ИМЕЕТ доступа к контексту экземпляра компонента `this`,
// так как к моменту вызова экземпляр ещё не создан!
},
beforeRouteUpdate (to, from, next) {
// вызывается когда маршрут, что рендерит этот компонент изменился,
// но этот компонент будет повторно использован в новом маршруте.
// Например, для маршрута с динамическими параметрами `/foo/:id`, когда мы
// перемещаемся между `/foo/1` и `/foo/2`, экземпляр того же компонента `Foo`
// будет использован повторно, и этот хук будет вызван когда это случится.
// Также имеется доступ в `this` к экземпляру компонента.
},
beforeRouteLeave (to, from, next) {
// вызывается перед переходом от пути, соответствующего текущему компоненту;
// имеет доступ к контексту экземпляра компонента `this`.
}
}
beforeRouteEnter (to, from, next) {
next(vm => {
// экземпляр компонента доступен как `vm`
})
}
export const $axios: AxiosInstance = _axios
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import Vue from 'vue'
// Full config: https://github.com/axios/axios#request-config
// axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || '';
// axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
/* eslint-disable */
const config = {
baseURL: process.env.VUE_APP_API,
timeout: 30000,
validateStatus (status: number) {
return status < 500 // Resolve only if the status code is less than 500
}
}
/* eslint-enable */
const _axios: AxiosInstance = axios.create(config)
/* eslint-disable */
// @ts-ignore
_axios.interceptors.request.use(async (config: AxiosRequestConfig): AxiosRequestConfig | Promise<AxiosRequestConfig> => {
return Promise.resolve(config)
}, (error) => {
// Do something with request error
return Promise.reject(error)
}
)
/* eslint-disable */
// Add a response interceptor
_axios.interceptors.response.use((response): Promise<AxiosResponse> | any => {
return response
}, (error) => {
// Do something with response error
return Promise.reject(error)
}
)
class AxiosPlugin {
public install () {
Object.defineProperties(Vue.prototype, {
axios: {
get () {
return _axios
}
},
$axios: {
get () {
return _axios
}
}
})
}
}
const axiosPlugin: AxiosPlugin = new AxiosPlugin()
Vue.use(axiosPlugin)
export default axiosPlugin
export const $axios: AxiosInstance = _axios
CREATE TABLE `persons` (
`id` INT(10,0) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL COLLATE 'utf8mb4_0900_ai_ci',
`phone` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_0900_ai_ci',
PRIMARY KEY (`id`) USING BTREE
)
COLLATE='utf8mb4_0900_ai_ci'
ENGINE=InnoDB;
CREATE TABLE `orders` (
`id` INT(10,0) NOT NULL AUTO_INCREMENT,
`date` DATETIME NULL DEFAULT NULL,
`person_id` INT(10,0) NOT NULL DEFAULT '0',
`street` VARCHAR(255) NOT NULL DEFAULT '0' COLLATE 'utf8mb4_0900_ai_ci',
`name` VARCHAR(255) NOT NULL DEFAULT '0' COLLATE 'utf8mb4_0900_ai_ci',
`phone` VARCHAR(50) NOT NULL DEFAULT '0' COLLATE 'utf8mb4_0900_ai_ci',
PRIMARY KEY (`id`) USING BTREE,
INDEX `person_id` (`person_id`) USING BTREE,
CONSTRAINT `FK1_OP` FOREIGN KEY (`person_id`) REFERENCES `rmok`.`persons` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
)
COLLATE='utf8mb4_0900_ai_ci'
ENGINE=InnoDB;
model: {
prop: 'selected',
event: 'change'
},
<template>
<v-autocomplete
v-model="selected"
:items="users"
:search-input.sync="usersSearchQuery"
persistent-hint
:label="label"
:rules="rules"
:loading="loading"
>
<template
v-slot:prepend
v-if="visibleIcon && ['lg', 'md'].includes($vuetify.breakpoint.name)"
>
<v-icon class="pl-5 pr-9">mdi-account-tie</v-icon>
</template>
<template v-slot:selection="{ attr, on, item }">
<span>{{ item.first_name }} {{ item.last_name }}</span>
</template>
<template v-slot:item="{ item }">
<v-list-item-avatar
color="indigo"
class="headline font-weight-light white--text"
>
{{ item.first_name.charAt(0) }}
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title>{{ item.first_name }} {{ item.last_name }}</v-list-item-title>
<v-list-item-subtitle
v-if="item.role"
v-text="item.role.name"
></v-list-item-subtitle>
</v-list-item-content>
</template>
</v-autocomplete>
</template>
<script lang="ts">
import Vue from 'vue'
import { debounce } from 'vuetify/src/util/helpers'
import { Users } from '@/api/Users'
export default Vue.extend({
name: 'UserAutocomplete',
model: {
prop: 'selected',
event: 'change'
},
props: {
selectedId: {
type: Number,
default: 0
},
search: {
type: String,
default: ''
},
rules: {
type: Array,
default: undefined
},
visibleIcon: {
type: Boolean,
default: false
},
label: {
type: String,
default: ''
}
},
data () {
return {
loading: false,
selectOnce: false,
selected: null,
usersSearchQuery: null,
usersProcessLoading: false,
usersSearchDebounce: debounce((q: string) => {
this.loading = true
new Users().findUsers(q)
.then(({ count, items }) => {
this.users = items
if (this.selectOnce === false) {
this.selectOnce = true
this.selected = this.users.find((e) => e.id === this.selectedId)
console.log(this.selectedId, this.users)
console.log(this.selected)
}
}).finally(() => {
this.loading = false
})
}, 400),
users: []
}
},
watch: {
selected (value) {
this.$emit('change', value)
},
usersSearchQuery (val: string) {
this.usersSearchDebounce(val)
}
},
created () {
this.usersSearchQuery = this.search
}
})
</script>
Vue.observable({
count: 0
})
class JsSIPlugin {
public install () {
Object.defineProperties(Vue.prototype, {
$jsSIP: {
get (): JsSIP {
return $jssip
}
}
})
Vue.observable($jssip)
}
}
security:
encoders:
App\Entity\User:
algorithm: auto
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
app_security_user_provider:
id: App\Security\UserProvider
# registration:
# pattern: ^/secure.registration
# stateless: true
# anonymous: true
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
authorization:
pattern: ^/authorization
security: false
main:
anonymous: ~
stateless: true
provider: app_security_user_provider
guard:
authenticators:
- App\Security\Authenticator
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
# - { path: ^/profile, roles: ROLE_USER }
// throw exception
$this->denyAccessUnlessGranted(UserVoter::READ["read"], User::class, "Вам запрещено просматривать пользователей.");
// or
// return boolean
$this->isGranted(UserVoter::READ["read"], User::class)