• В Symfony лучше создать одну большую миграцию или много маленьких(отдельную для каждой таблицы)?

    firedragon
    @firedragon
    Не джун-мидл-сеньор, а трус-балбес-бывалый.
    Миграция подразумевает одно изменение одну фичу. Она должна быть отменяемая и содержать все данные.
    Поэтому фича должна быть маленькой, а миграция включать все таблицы которые изменились
    Ответ написан
    Комментировать
  • Как работать с изображениями в веб-приложениях на Java?

    azerphoenix
    @azerphoenix Куратор тега Java
    Java Software Engineer
    Добрый день!

    Как хранить изображение? Насколько я знаю, их можно запихивать прямо в PostgreSQL, но я склоняюсь в сторону того, что изображения надо сохранять в ресурсы, а в бд хранить только ссылки.

    Не самая лучшая идея хранить картинки в виде binary в БД. Лучше, как вы отметили в БД хранить ссылку на объект, а ресурс в файловой системе. Можно хранить файлы в распределенных файловых системах (AWS, Google), но вряд ли вы располагаете таким бюджетом.

    Вот, простая реализация хранения картинок в FS.

    Entity
    @Entity
    @Data
    @NoArgsConstructor
    @Table(name = "attachments")
    public class Attachment {
    
      @Id
      @GeneratedValue(strategy = GenerationType.SEQUENCE)
      private Long attachId;
    
      private String attachTitle;
    
      @Column(nullable = false, updatable = false)
      private LocalDate uploadDate;
    
      private String extension;
    
      private String downloadLink;
    
    }


    Repository
    @Repository
    public interface AttachmentRepository extends JpaRepository<Attachment, Long> {}


    Service
    public interface AttachmentService {
    
      /**
       * Загрузить новый файл
       *
       * @param file
       * @param user
       * @throws IOException
       */
      Attachment addAttachment(MultipartFile file, User user) throws IOException;
    
    
      /**
       * Найти Вложение по его ID
       *
       * @param attachId
       * @return
       */
      Attachment findAttachById(Long attachId);	
    
      /**
       * Скачать файл
       *
       * @param uploadYear
       * @param fileName
       * @return
       * @throws MalformedURLException
       */
      Resource loadFileAsResource(String uploadYear, String fileName) throws MalformedURLException;
    
    }


    ServiceImpl
    @Service
    @RequiredArgsConstructor
    public class AttachmentServiceImpl implements AttachmentService {
    
      private final AttachmentRepository attachmentRepository;
      private final AppProperties appProperties;
      private final FileTools fileTools;
    
      /**
       * Загрузить новый файл
       *
       * @param file
       * @param user
       * @throws IOException
       */
      @Override
      public Attachment addAttachment(MultipartFile file, User user) throws IOException {
        // Создаем директорию если ее не существует
        File uploadDir = new File(appProperties.getUploadPath());
        // Если директория uploads не существует, то создаем ее
        if (!uploadDir.exists()) {
          uploadDir.mkdirs();
        }
        String curDate = LocalDateTime.now().toString();
        // Создаем уникальное название для файла и загружаем файл
        String fileName =
            "attach_" + curDate + "_" + file.getOriginalFilename().toLowerCase().replaceAll(" ", "-");
        file.transferTo(new File(uploadDir + "/" + fileName));
        Attachment attachment = Attachment.builder()
            .attachTitle(fileName)
            .uploadDate(LocalDate.now())
            .extension(fileTools.getFileExtension(file.getOriginalFilename()))
            .downloadLink("/attachments/get/" + Year.now() + "/" + fileName)
            .build();
        attachmentRepository.save(attachment);
        return attachment;
      }
    
    
      /**
       * Найти Вложение по его ID
       *
       * @param attachId
       * @return
       */
      @Override
      public Attachment findAttachById(Long attachId) {
        return attachmentRepository
            .findById(attachId)
            .orElseThrow(() -> new AttachmentNotFoundException("Attachment not found!"));
      }
    
    
      /**
       * Скачать файл
       *
       * @param fileName
       * @return
       * @throws MalformedURLException
       */
      @Override
      public Resource loadFileAsResource( String fileName)
          throws MalformedURLException {
        Path fileStorageLocation =
            Paths.get(appProperties.getUploadPath()).toAbsolutePath().normalize();
        Path filePath = fileStorageLocation.resolve(fileName).normalize();
        return new UrlResource(filePath.toUri());
      }
    
    }


    Conroller
    @Controller
    @RequiredArgsConstructor
    @RequestMapping("/attachments")
    public class AttachmentController {
    
      private final AttachmentService attachmentService;
      private final UserService userService;
    
      /**
       * Загрузить новое вложение
       *
       * @param file
       * @return
       * @throws IOException
       */
      @PostMapping(value = "/add", produces = "application/json")
      @ResponseBody
      public ResponseEntity<Map<String, String>> uploadAttachment(
          @RequestPart(value = "file") MultipartFile file)
          throws IOException {
        Attachment attachment = attachmentService.addAttachment(file);
        Map<String, String> attachmentStatus = new HashMap<>();
        attachmentStatus.put("status", "ok");
        attachmentStatus.put("attachId", attachment.getAttachId().toString());
        return ResponseEntity.ok(attachmentStatus);
      }
    
      /**
       * Получить ссылку на скачивание загруженного файла
       *
       * @param filename
       * @param request
       * @return
       * @throws IOException
       */
      @GetMapping("/get/{filename:.+}")
      public ResponseEntity<Resource> serveFile(
           @PathVariable String filename, HttpServletRequest request)
          throws IOException {
        Resource resource = attachmentService.loadFileAsResource(filename);
        String contentType;
        contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
        if (contentType == null) {
          contentType = "application/octet-stream";
        }
        return ResponseEntity.ok()
            .contentType(MediaType.parseMediaType(contentType))
            .header(
                HttpHeaders.CONTENT_DISPOSITION,
                "attachment; filename=\"" + resource.getFilename() + "\"")
            .body(resource);
      }
    }
    Ответ написан
    4 комментария
  • CRUD Hibernate, как сделать последовательный инкремент с обновлением?

    @Kot1que
    пишу на жаве
    Во-первых, не особо понятно зачем это вообще нужно.
    Тут не про хибернейт, а про базу данных, которую вы используете.

    Первичный ключ должен быть уникальным, в случае автоинкремента еще и последовательным (монотонно строго-возрастающим).

    Навскидку так сделать можно: вешаете триггер на удаление и двигаете айдишники строк, обновляете sequence после. Однако это очень тяжелая операция - представьте, что пользователь удалил первую строку, а у вас их миллион, вам придется поменять айдишники миллиона записей.

    Более того, если вы где-то в ссылке (например, /movies/123) будете использовать этот айди (а это обычный сценарий), то при удалении все такие ссылки станут уже невалидными.
    Ответ написан
    1 комментарий
  • Масштабирование sql базы: можно ли настроить в Spring Boot?

    sergey-gornostaev
    @sergey-gornostaev Куратор тега PostgreSQL
    Седой и строгий
    Во-первых, вы никак не должны настраивать это, как разработчик, должен DBA настроить СУБД. Во-вторых, вы уж простите, но уже к тому времени, когда уровень системы добирается до появления вопросов масштабирования хотя бы СУБД, ей нужны профессионалы с как минимум десятилетним стажем, которого у вас явно нет. Многие системы вообще никогда не добираются до этого уровня.
    Вы уверены, что ваша добралась?
    5f9e569e017c1068975776.jpeg
    Ответ написан
    4 комментария
  • Spring application.properties как сделать?

    azerphoenix
    @azerphoenix Куратор тега Spring
    Java Software Engineer
    Здравствуйте!
    Запросто...
    1) Создайте класс например,
    @Configuration
    @ConfigurationProperties(prefix = "custom")
    public class AppProperties {
    
    // названия полей должны соответствовать названиям из properties. 
    private String baseUrl;
    private String uploadPath;
    
    // тут геттеры 
    }


    А вот, пример application.properties
    custom.base_url=http://localhost:8080
    custom.upload_path=/home/example.com/uploads


    Spring сам уже связывает base_url к baseUrl и т.д.
    Единственное, @ConfigurationProperties(prefix = "custom")
    тут в prefix укажите тот префикс, который вам нужен. Т.е. тут custom.base_url custom является префиксом.

    Подробности тут - https://www.baeldung.com/configuration-properties-...
    Ответ написан
    3 комментария
  • Как найти контроллер формы в symfony?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Данный контроллер, указанный в шаблоне отрисовывает (создает) форму по пути bundle:controller:action, а вот обрабатывает другой/другие. Вообще текущий шаблон вызывается искомым контроллером или несколькими.

    Ищем контроллер по обработке формы:

    1a. Или открываете гугл-консоль вкладку Network -> XHR и при отправке формы смотрите — на какой роут идет отправка
    1b. Или открываете дебаг-консоль Symfony и при отправке формы смотрите — на какой роут идет отправка

    2. Ищите роут: bin/consol debug:route <route>, где <route> — путь от path, можно через grep искать: bin/console d:r | grep <route_part>

    Команда выдаст вам примерно такую инфу:
    $ bin/console d:r home
    
    +--------------+----------------------------------------------------------------+
    | Property     | Value                                                          |
    +--------------+----------------------------------------------------------------+
    | Route Name   | home                                                           |
    | Path         | /{_locale}                                                     |
    | Path Regex   | {^/(?P<_locale>en|ru)?$}sDu                                    |
    | Requirements | _locale: en|ru                                                 |
    | Class        | Symfony\Component\Routing\Route                                |
    | Defaults     | _controller: \App\Controller\HomeController::index() |
    |              | _locale: en                                                    |
    | Options      | compiler_class: Symfony\Component\Routing\RouteCompiler        |
    |              | utf8: true                                                     |
    +--------------+----------------------------------------------------------------+


    но там могут быть переменные (id например или слаг), нужно без них... в любом случае путь найдете по команде bin/consol debug:route название вашего роута (справа пути с выражениями, слева названия роута — его и подставить в первую команду нужно):
    users.show           ANY      ANY      ANY    /admin/users/{id}                                 
    app_login            ANY      ANY      ANY    /login                                            
    app_logout           GET      ANY      ANY    /logout                                                               
    oauth.fake           ANY      ANY      ANY    /fake_login                                           
    auth.signup          ANY      ANY      ANY    /signup


    3. Там где-то есть обработка формы, находите что-то типа if ($form->isSubmitted() && $form->isValid()) и уже внедряете свою капчу
    4. PROFIT!
    Ответ написан
    7 комментариев
  • На чём лучше поднять домашний сервер?

    ky0
    @ky0
    Миллиардер, филантроп, патологический лгун
    Проблема в том. что уже запарился гуглить по каждой мелочи

    Это нормальный процесс наращивания объёма знаний. Нет никакой серебряной пули в виде волшебной ОС, которая решит все ваши текущие и будущие проблемы.
    Ответ написан
    Комментировать
  • Как реализовать "защиту" авторизации по номеру телефона?

    Vamp
    @Vamp
    1. Проверьте синтаксическую корректность номера телефона. Все мобильные номера в РФ начинаются на +79 и имеют длину ровно 11 цифр. Проверку можно даже добавить в веб форму на уровне js. Это нельзя назвать защитой от хулиганов, но она отсеет реальные ошибки и опечатки, облегчив жизнь обычным пользователям.

    2. Пробейте номер по базе россвязи (файл DEF-9xx). Так вы определите номера, на которые 100% не будет доставки. В отличии от проверки синтаксиса, не выдавайте пользователю ответ о некорректном номере. На все номера отвечайте "Одноразовый код отправлен, введите его сюда", но на невалидные номера не отправляйте сообщение.

    3. Добавьте ограничение на количество отправляемых форм в минуту с одного IP и количество отправляемых сообщений на один и тот же номер (независимо от IP).

    4. Оцените сколько может быть отправок форм в день и поставьте общий лимит на все отправки смс за день. Это единственный реальный способ контролировать атаку на сливание бюджета. Да, реальные пользователи пострадают при достижении лимита, но вы не должны доводить до него - настройте мониторинг количества отправляемых смс и алертинг при достижении порога в 90% от лимита, чтобы у вас было время среагировать на атаку и отбить её до полного исчерпания лимита. Либо если это всплеск реальных пользователей (например, неожиданно удачная реакция на рекламу), то у вас будет время скорректировать лимит. Можно рассчитывать лимит как 2 * среднее количество отправок смс за последние Х дней, чтобы не приходилось править его вручную по мере естественного роста посещаемости. Формулу и процент для алертинга, разумеется, подберёте под свои требования. Но можете взять и мои за основу.

    Отдельно хочу рассказать про так называемые прямые мобильные номера. Они выглядят как городские (например, +7495), но в реальности являются мобильными и могут принимать смски. Проверка в пункте 1 не пропускает такие номера и нет никакого способа проверить без отправки смс является ли отдельно взятый городской номер прямым мобильным. Прямых номеров мало по сравнению с настоящими мобильными или настоящими городскими. К тому же у каждого прямого номера есть мобильный аналог, начинающийся на +79, которым пользователь может воспользоваться для регистрации. Поэтому предлагаю просто забить на прямые номера, а в случае жалоб на невозможность регистрации с прямым номером, рассказывать про существование мобильного аналога, который может быть прописан где-то в договоре с оператором на оказание услуг связи или узнать в техподдержке оператора и с которым можно спокойно зарегистрироваться.

    Обязательное требование email'а не усилит схему защиты, так как не проблема наштамповать реальных адресов со скриптом, автоматически прокликивающим подтверждающие ссылки во входящих письмах.

    Альтернативным вариантом является аренда входящего номера. В этом случае не вы отправляете сообщения пользователям, а они вам. Обычно за входящий номер берут фиксированную плату в месяц независимо от количества смс, так что не придётся в принципе волноваться за бюджет. Но тогда у вас будут в пролёте пользователи с отключенной услугой отправки смс. А таких немало, могу сказать. Благодаря интернет-мессенджерам.
    Ответ написан
    2 комментария
  • Как называется расширение файла для запуска html на компьютере без браузера?

    zkrvndm
    @zkrvndm
    Архитектор решений
    Как один из вариантов, вы можете заверстать обычный веб-сайт с нужным вам функционалом и запихнуть файлы вашего сайта в Electron. Будет открываться на любом ПК, даже если там нет браузера. Все это без адресной строки, словно это программа, хотя это и будет программа, пусть и написанная чисто на HTML/JavaScript. Сам по себе Electron представляет собой настраиваемый в широких пределах браузер, отображающий то, что в него вложите и именно так, как вам нужно. Плюс, возможность получить доступ к вашему железу, файловой системе и другим вещам, чисто через JavaScript.

    Подробнее тут.
    Ответ написан
    Комментировать
  • GitKraken где найти материал обучения?

    GavriKos
    @GavriKos
    Если вы знаете git - то с gitkraken не должно быть никаких проблем.
    Если не знаете git - ищите по нему обучалки - их ПОЛНО
    Ответ написан
    Комментировать
  • Не знаю как вызвать метод из другого класса?

    sergey-gornostaev
    @sergey-gornostaev Куратор тега Java
    Седой и строгий
    Arithmetic.check(new int[] { 4 });

    Вам бы учебник почитать.
    Ответ написан
    2 комментария
  • Какую использовали программу для дизайна квартиры?

    Sketchup
    Все модели мебели ищутся в его 3D warehouse
    Ответ написан
    Комментировать
  • Нужно определиться подходит ли для высоконагруженного проекта: Symphony + React + MongoDB + UIkit + Onsen UI?

    kawabanga
    @kawabanga
    500К пользователей ни о чем не говорят.

    Проблема что настораживает что на Yii особо крупного ничего не писали

    Изучите вопрос внимателей, писали. Но другое дело, что сейчас тренд на отказ от yii. к сожалению. Непонятно, как будет с v3.

    многие недолюбливают из за черезмерной сложности и.т.д.

    react то?

    с БД вы кажется определились. Других вопросов не вижу.
    Ответ написан
    Комментировать
  • Как описать приготовление яичницы в терминах MVC?

    @Urukhayy
    С течением времени было множество трактовок.
    Одним из примеров MVC является - игровая приставка.

    Model - сама приставка
    View - телевизор, который подключается к приставке
    Controller - контроллер для игровой приставки (манипулятор), так и называется. Неофициально - "джойстик".

    Ключевым моментом является Model (сама приставка), в ней происходят все сложные процессы и вычисления, хранение данных и просчёты.
    View, что в соответствии телевизор, - это лишь средство представления информации с вида, удобного для приставки (нули и единицы), в вид, удобный для человека.
    Controller, что в соответствии игровой контроллер, - это способ преобразования команд с вида, удобного для человека (вперёд, прыжок), в вид, удобный для приставки.

    Модель, в принципе, подразумевает у неё самой наличие функций, позволяющих ей меняться. Контроллер лишь командует - в какой момент времени каким функциям нужно вызваться, чтобы поменять модель. Если убрать из системы игровой контроллер, и попытаться отвёрткой замыкать контакты в гнезде игровой приставки (т. е. обратиться напрямую в модель, минуя контроллер), пытаясь тем самым "управлять персонажем", то это будет затруднительно - куда проще нажать на кнопку, а контроллер преобразует это в нужный вид.

    Картинка из вики:

    MVC-Process.png
    Ответ написан
    Комментировать
  • Зачем в дженериках используется ?, и эквивалентен ли он T/E?

    @engineerVadim
    ? extends U - некоторый тип, который является потомком U, ? super T - некоторый тип, который является предком Т. Ваша схема(так как у Вас написано), как правило применяется: первая часть для приемника, вторая для источника при обработке чего либо.
    Ответ написан
    Комментировать
  • Суть виртуальной машины Java?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Тут дали уже много хороших и правильных ответов, но хотелось бы уточнить, что вот эта метафора:
    Виртуальня машина java это тоже интерпретатор по сути

    может направить по весьма ложному пути!

    У слов в названиях есть достаточно точный смысл, и JVM называется именно машиной, а не интерпретатором, и не компилятором совершенно не случайно. Компилятор в Яве есть (javac), и он нужен не для выполнения программы, а именно для ее компиляции (в байткод). Имено поэтому он не входит в состав JRE (среды выполнения), а содержится в JDK (среде разработки). В самой JVM есть еще один, JIT-компилятор, который компилирует байткод в инструкции процессора во время выполнения программы, но это уже другая история, и его тоже никак не назвать интерпретатором.

    По сути JVM - это процессор, только виртуальный. И как у любого процессора (железного, типа x86, или виртуального, типа CLR в .NET), у него есть свой набор опкодов, называемый байткодом. Так же точно, как на х86 может выполняться код, порожденный компилятором с C++, или Pascal, или Go, так же и на JVM может выполняться байткод, скомпилированный из Java, или Scala, или Kotlin (или даже написанный вручную), а .class -файл, это, по сути, тот же .exe (точнее .so), скомпилированный под "процессор JVM". В этом и заключается кроссплатформенность. Так же, как код, скомпилированный под х86 будет выполняться на процессоре от Intel или AMD, так же и байткод JVM будет выполняться на JVM от Oracle, IBM, OpenJDK и т.д. И даже наличие JIT, компилирующего байткод в опкод конкретного железного процессора во время выполнения, все еще не дает повода обзывать честную стековую (SUN) или регистровую (Dalvik) VM интерпретатором, пусть даже и по сути :)

    Дело в том, что сама эта классификация (интерпретируемый/компилируемый ЯЗЫК) последние лет эдак 25 уже практически лишена смысла. Языкам, изначально ориентированным на реализацию в виде интерпретатора (с просто анализируемой лексикой, чтоб интерпретатор был поменьше и мог оставить самой программе достаточно места в ограниченной по объему памяти) типа APL или BASIC, сейчас (кроме, разумеется, очень узкоспециального применения) почетное место разве что в старых учебниках, из которых эту самую классификацию, с достойным лучшего применения упорством, продолжают дословно переписывать в новые. При этом, почему-то, забывают уточнить, что эти два понятия уже давно не про сами языки, а всего лишь про некоторые методы их реализации, и что с тех пор помимо этих методов появилось еще много других хороших и разных концепций на эту тему (типа VM, JIT, сборщиков мусора, да и хотя бы тех же OOП, разных видов типизации и еще миллион чего), которых в тех учебниках еще просто не было в силу их года издания. И что на сегодня уже даже для языков, принципиально заточеных для компиляции под регистровую архитектуру, типа С, есть пруд пруди интерпретаторов (раз, два, три)... которые, опять же, никто не называет виртуальными машинами, т.к. это все суть разные понятия. Короче, это все равно что пытаться понять, где в квантовой механике огонь, вода, земля и воздух, в том виде, как их понимали Платон и Аристотель :)

    P.S. Чтоб осознать, когда эта классификация еще была актуальна, рекомендую вот это . Там создатели APL, одного из первых настоящих интерпретируемых языков, обсуждают насущные проблемы языкостроения того времени. Если туго с английским, посмотрите хотя бы вступление... в тех железяках было меньше памяти и вычислительной мощности, чем в современной симке :)
    Ответ написан
    Комментировать
  • Как правильно построить архитектуру Symfony 4?

    @dreamerz
    Как я собираю проэкт Симфонии
    composer self-update
    composer require symfony/symfony-skeletone (требует лимит мемори для пхп свыше 1800М)
    Никогда неделайте ничего при старте вручную - Первое правило =)
    Если Вам вдруг нужна авторизация из-корробки -
    php bin/console make:auth
    Первый Ваш контроллер
    php bin/console make:controller
    > MyShopController
    Нужен класс для связи с БД
    php bin/console make:entity
    > Shop
    в процессе Вам напишут что Реппозиторий создался автомматически
    Мы забыли про БД! Непугайтесь сделайте ещё пару комманд)
    php bin/console doctrine:database:create
    Теперь можно выгружать таблицы:
    php bin/console make:migration
    Теперь чтобы увидеть таблицы в самой БД скажем комманду
    php bin/console doctrine:migrations:migrate
    Вуаля - это Магия Симфонии)

    Если что - обращайтесь

    Если конкретно по-вопросу -
    У меня роутер выглядит так:
    app_product
    path: /product/{slug}/{action}
    controller: App\Controller\ProductController:indexAction

    По ссылке /product/update/#id
    POST ajax request
    ProductController:
    $post = $request->request->all(); // все POST запросы
    $product = new Product;
    $product->setPrice($post['price']);
    ...
    /product/show/#id
    Загружается страничка с продуктом.
    Это конкретный пример логики
    Ответ написан
  • С чем связанна странная манера сайтов дорогих брендов?

    @ilovemaryjane
    А вы что ожидали увидеть? Это дерьмо, пропихиваемое мафией с awwwards? Эти омерзенные сайты с отключенным скроллом или скроллджекингом, загружающиеся не менее минуты, с WebGL от которого все виснет и закипает процессор и состоящие на 90% из мигающего видео на заднем плане?
    Ответ написан
    Комментировать