Compare commits
24 Commits
7998209e95
...
master
Author | SHA1 | Date | |
---|---|---|---|
f797e1718b
|
|||
19c2801856
|
|||
63a324c437
|
|||
acb99ca6f1
|
|||
3b001cc2fb
|
|||
f523f6c39a
|
|||
fdb82bddf5
|
|||
7482786c22
|
|||
b9cc69c160
|
|||
3d26e338e2
|
|||
0ea97e907e
|
|||
786efb4503
|
|||
e4b27c55bf
|
|||
221d408ae3
|
|||
c6212165af
|
|||
0cb732c803
|
|||
2c4ca426fd
|
|||
c8da8fa23f
|
|||
6a7047cfe9
|
|||
7007b184d0
|
|||
91cfa35d2c | |||
da98a36142 | |||
d37b3473a4 | |||
7676933e40 |
@ -1,80 +0,0 @@
|
|||||||
version: 2.1
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build_assets:
|
|
||||||
docker:
|
|
||||||
- image: node:12-alpine
|
|
||||||
environment:
|
|
||||||
OUTPUT_DIR: ./output_prod/static
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- run: npm ci
|
|
||||||
- run: mkdir -p ${OUTPUT_DIR}
|
|
||||||
- run: npm run-script build-prod
|
|
||||||
- run: ls -la ${OUTPUT_DIR}
|
|
||||||
- run: mkdir -p /tmp/workspace/static
|
|
||||||
- run: cp -R ${OUTPUT_DIR}/* /tmp/workspace/static
|
|
||||||
- persist_to_workspace:
|
|
||||||
root: /tmp/workspace
|
|
||||||
paths:
|
|
||||||
- static/*
|
|
||||||
|
|
||||||
build_html:
|
|
||||||
docker:
|
|
||||||
- image: php:7.4-cli
|
|
||||||
environment:
|
|
||||||
APP_ENV: prod
|
|
||||||
APP_URL: https://vakhrushev.me
|
|
||||||
STATIC_DIR: ./output_prod/static
|
|
||||||
steps:
|
|
||||||
- attach_workspace:
|
|
||||||
at: /tmp/workspace
|
|
||||||
- checkout
|
|
||||||
- run: docker/provision.sh
|
|
||||||
- run: composer install --no-interaction --no-progress
|
|
||||||
- run: mkdir -p ${STATIC_DIR}
|
|
||||||
- run: cp -R /tmp/workspace/static/* ${STATIC_DIR}
|
|
||||||
- run: |
|
|
||||||
./vendor/bin/sculpin generate \
|
|
||||||
--env="${APP_ENV}" \
|
|
||||||
--url="${APP_URL}" \
|
|
||||||
--no-interaction \
|
|
||||||
-vv
|
|
||||||
- run: mkdir -p /tmp/workspace/html
|
|
||||||
- run: cp -R ./output_prod/* /tmp/workspace/html
|
|
||||||
- persist_to_workspace:
|
|
||||||
root: /tmp/workspace
|
|
||||||
paths:
|
|
||||||
- html/*
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
docker:
|
|
||||||
- image: php:7.4-cli
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- add_ssh_keys
|
|
||||||
- run: apt-get update; apt-get install -yy openssh-client
|
|
||||||
- run: ssh-keyscan "vakhrushev.me" >> ~/.ssh/known_hosts
|
|
||||||
- run: docker/provision.sh
|
|
||||||
- attach_workspace:
|
|
||||||
at: /tmp/workspace
|
|
||||||
- run: mkdir -p ./output_prod
|
|
||||||
- run: cp -R /tmp/workspace/html/* ./output_prod
|
|
||||||
- run: ls -la ./output_prod
|
|
||||||
- run: ls -la ./output_prod/static
|
|
||||||
- run: dep deploy production -vv
|
|
||||||
|
|
||||||
workflows:
|
|
||||||
version: 2
|
|
||||||
build_and_deploy:
|
|
||||||
jobs:
|
|
||||||
- build_assets:
|
|
||||||
filters:
|
|
||||||
branches:
|
|
||||||
only: master
|
|
||||||
- build_html:
|
|
||||||
requires:
|
|
||||||
- build_assets
|
|
||||||
- deploy:
|
|
||||||
requires:
|
|
||||||
- build_html
|
|
@ -1,4 +1,3 @@
|
|||||||
/node_modules
|
/node_modules
|
||||||
/output_*
|
|
||||||
/var
|
/var
|
||||||
/vendor
|
/vendor
|
||||||
|
@ -6,10 +6,7 @@ insert_final_newline = true
|
|||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
[*.{html,twig,yml,xml}]
|
[*.{html,twig,yml,yaml,xml}]
|
||||||
indent_size = 2
|
|
||||||
|
|
||||||
[gulpfile.js]
|
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
[package.json]
|
[package.json]
|
||||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,7 +1,13 @@
|
|||||||
.idea/
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
.cache/
|
||||||
|
.config/
|
||||||
|
.home/
|
||||||
|
|
||||||
output_*
|
output_*
|
||||||
node_modules/
|
node_modules/
|
||||||
var/
|
var/
|
||||||
vendor/
|
vendor/
|
||||||
|
|
||||||
.php_cs.cache
|
.php_cs.cache
|
||||||
|
.php-cs-fixer.cache
|
||||||
|
@ -7,7 +7,7 @@ $finder = PhpCsFixer\Finder::create()
|
|||||||
->in(__DIR__.'/bundle')
|
->in(__DIR__.'/bundle')
|
||||||
;
|
;
|
||||||
|
|
||||||
return PhpCsFixer\Config::create()
|
return (new PhpCsFixer\Config())
|
||||||
->setFinder($finder)
|
->setFinder($finder)
|
||||||
->setRules([
|
->setRules([
|
||||||
'@Symfony' => true,
|
'@Symfony' => true,
|
76
Makefile
76
Makefile
@ -1,76 +0,0 @@
|
|||||||
APP_ENV := dev
|
|
||||||
APP_OUTPUT_DIR := output_dev
|
|
||||||
APP_URL := homepage.site
|
|
||||||
APP_NPM_BUILD_CMD := build
|
|
||||||
|
|
||||||
ifeq ($(TARGET), prod)
|
|
||||||
APP_ENV := prod
|
|
||||||
APP_OUTPUT_DIR := output_prod
|
|
||||||
APP_URL := https://vakhrushev.me
|
|
||||||
APP_NPM_BUILD_CMD := build-prod
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Installation
|
|
||||||
|
|
||||||
install: build-docker install-php-deps install-js-deps
|
|
||||||
|
|
||||||
build-docker:
|
|
||||||
./tools/build-docker
|
|
||||||
|
|
||||||
install-php-deps:
|
|
||||||
./tools/composer install --no-interaction
|
|
||||||
|
|
||||||
install-js-deps:
|
|
||||||
./tools/npm ci
|
|
||||||
|
|
||||||
# Building
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf ./${APP_OUTPUT_DIR}/*
|
|
||||||
|
|
||||||
build-assets:
|
|
||||||
./tools/npm run "${APP_NPM_BUILD_CMD}"
|
|
||||||
|
|
||||||
build-site:
|
|
||||||
./tools/sculpin generate \
|
|
||||||
--env="${APP_ENV}" \
|
|
||||||
--url="${APP_URL}" \
|
|
||||||
--no-interaction \
|
|
||||||
-vv
|
|
||||||
|
|
||||||
build: clean build-assets build-site
|
|
||||||
|
|
||||||
build-prod:
|
|
||||||
$(MAKE) build TARGET=prod
|
|
||||||
|
|
||||||
# Format
|
|
||||||
|
|
||||||
format-pages:
|
|
||||||
./tools/npm run format-md
|
|
||||||
|
|
||||||
format-assets:
|
|
||||||
./tools/npm run format-webpack
|
|
||||||
./tools/npm run format-js
|
|
||||||
./tools/npm run format-vue
|
|
||||||
./tools/npm run format-style
|
|
||||||
|
|
||||||
format-php:
|
|
||||||
./tools/php-cs-fixer fix
|
|
||||||
|
|
||||||
format: format-pages format-assets format-php
|
|
||||||
|
|
||||||
watch: clean build-assets
|
|
||||||
./tools/sculpin generate \
|
|
||||||
--env="${APP_ENV}" \
|
|
||||||
--watch \
|
|
||||||
--server \
|
|
||||||
--port=8000 \
|
|
||||||
--no-interaction
|
|
||||||
|
|
||||||
# Deploy
|
|
||||||
|
|
||||||
deploy: build-prod
|
|
||||||
./tools/dep deploy production -vv
|
|
||||||
|
|
||||||
rollback:
|
|
||||||
./tools/dep rollback production -vv
|
|
@ -1,10 +1,10 @@
|
|||||||
Source for [vakhrushev.me](http://vakhrushev.me).
|
Source for [vakhrushev.me](https://vakhrushev.me).
|
||||||
|
|
||||||
Build:
|
Build:
|
||||||
|
|
||||||
make build-docker
|
task build-docker
|
||||||
make build
|
task build
|
||||||
|
|
||||||
Deploy:
|
Deploy:
|
||||||
|
|
||||||
make deploy
|
task deploy
|
||||||
|
130
Taskfile.yml
Normal file
130
Taskfile.yml
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
# https://taskfile.dev
|
||||||
|
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
vars:
|
||||||
|
USER_ID:
|
||||||
|
sh: id -u
|
||||||
|
GROUP_ID:
|
||||||
|
sh: id -g
|
||||||
|
|
||||||
|
PROJECT: "homepage"
|
||||||
|
PHP_IMAGE: "{{.PROJECT}}-php"
|
||||||
|
NODE_IMAGE: "{{.PROJECT}}-node"
|
||||||
|
|
||||||
|
DOCKER_COMMON_OPTS: >-
|
||||||
|
--rm
|
||||||
|
--interactive
|
||||||
|
--tty
|
||||||
|
--user {{.USER_ID}}:{{.GROUP_ID}}
|
||||||
|
--volume /etc/passwd:/etc/passwd:ro
|
||||||
|
--volume /etc/group:/etc/group:ro
|
||||||
|
--volume "./:/srv/app"
|
||||||
|
--workdir "/srv/app"
|
||||||
|
-e XDG_CONFIG_HOME=/srv/app/.config
|
||||||
|
-e XDG_CACHE_HOME=/srv/app/.cache
|
||||||
|
-e HOME=/srv/app/.home
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
|
||||||
|
build-docker:
|
||||||
|
cmds:
|
||||||
|
- docker build --file docker/php/Dockerfile --tag "{{.PHP_IMAGE}}" .
|
||||||
|
- docker build --file docker/node/Dockerfile --tag "{{.NODE_IMAGE}}" .
|
||||||
|
|
||||||
|
composer:
|
||||||
|
cmds:
|
||||||
|
- docker run {{.DOCKER_COMMON_OPTS}} "{{.PHP_IMAGE}}" composer {{.CLI_ARGS}}
|
||||||
|
|
||||||
|
npm:
|
||||||
|
cmds:
|
||||||
|
- docker run {{.DOCKER_COMMON_OPTS}} "{{.NODE_IMAGE}}" npm {{.CLI_ARGS}}
|
||||||
|
|
||||||
|
sculpin:
|
||||||
|
cmds:
|
||||||
|
- docker run {{.DOCKER_COMMON_OPTS}} "{{.PHP_IMAGE}}" ./vendor/bin/sculpin {{.CLI_ARGS}}
|
||||||
|
|
||||||
|
shell-node:
|
||||||
|
cmds:
|
||||||
|
- docker run {{.DOCKER_COMMON_OPTS}} "{{.NODE_IMAGE}}" bash
|
||||||
|
|
||||||
|
install-dependencies:
|
||||||
|
cmds:
|
||||||
|
- task: composer
|
||||||
|
vars: { CLI_ARGS: "install" }
|
||||||
|
- task: npm
|
||||||
|
vars: { CLI_ARGS: "install" }
|
||||||
|
|
||||||
|
format-pages:
|
||||||
|
cmds:
|
||||||
|
- task: npm
|
||||||
|
vars: { CLI_ARGS: 'run format-md' }
|
||||||
|
|
||||||
|
format-assets:
|
||||||
|
cmds:
|
||||||
|
- task: npm
|
||||||
|
vars: { CLI_ARGS: 'run format-webpack' }
|
||||||
|
- task: npm
|
||||||
|
vars: { CLI_ARGS: 'run format-js' }
|
||||||
|
- task: npm
|
||||||
|
vars: { CLI_ARGS: 'run format-vue' }
|
||||||
|
- task: npm
|
||||||
|
vars: { CLI_ARGS: 'run format-style' }
|
||||||
|
|
||||||
|
format-php:
|
||||||
|
cmds:
|
||||||
|
- docker run {{.DOCKER_COMMON_OPTS}} "{{.PHP_IMAGE}}" php-cs-fixer fix
|
||||||
|
|
||||||
|
build-dev:
|
||||||
|
vars:
|
||||||
|
APP_OUTPUT_DIR: output_dev
|
||||||
|
NPM_SCRIPT: build
|
||||||
|
APP_ENV: dev
|
||||||
|
APP_URL: homepage.site
|
||||||
|
cmds:
|
||||||
|
- rm -rf ./{{.APP_OUTPUT_DIR}}/*
|
||||||
|
- task: npm
|
||||||
|
vars: { CLI_ARGS: 'run {{.NPM_SCRIPT}}' }
|
||||||
|
- task: sculpin
|
||||||
|
vars: { CLI_ARGS: 'generate --env="{{.APP_ENV}}" --url="{{.APP_URL}}" --no-interaction -vv' }
|
||||||
|
|
||||||
|
build-prod:
|
||||||
|
vars:
|
||||||
|
APP_OUTPUT_DIR: output_prod
|
||||||
|
NPM_SCRIPT: build-prod
|
||||||
|
APP_ENV: prod
|
||||||
|
APP_URL: https://vakhrushev.me
|
||||||
|
cmds:
|
||||||
|
- rm -rf ./{{.APP_OUTPUT_DIR}}/*
|
||||||
|
- task: npm
|
||||||
|
vars: { CLI_ARGS: 'run {{.NPM_SCRIPT}}' }
|
||||||
|
- task: sculpin
|
||||||
|
vars: { CLI_ARGS: 'generate --env="{{.APP_ENV}}" --url="{{.APP_URL}}" --no-interaction -vv' }
|
||||||
|
|
||||||
|
make-post:
|
||||||
|
vars:
|
||||||
|
POST_DATE:
|
||||||
|
sh: date +'%Y-%m-%d'
|
||||||
|
cmd: touch "source/_articles/{{.POST_DATE}}-new-post.md"
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
vars:
|
||||||
|
COMMIT_HASH:
|
||||||
|
sh: git rev-parse --short HEAD
|
||||||
|
TIMESTAMP:
|
||||||
|
sh: date +%s
|
||||||
|
DOCKER_IMAGE: homepage-nginx:{{.COMMIT_HASH}}-{{.TIMESTAMP}}
|
||||||
|
cmds:
|
||||||
|
- task: build-prod
|
||||||
|
- docker build --pull --file docker/Dockerfile.nginx.prod --tag {{.DOCKER_IMAGE}} .
|
||||||
|
- task: deploy-with-ansible
|
||||||
|
vars:
|
||||||
|
DOCKER_IMAGE: '{{.DOCKER_IMAGE}}'
|
||||||
|
|
||||||
|
|
||||||
|
deploy-with-ansible:
|
||||||
|
internal: true
|
||||||
|
requires:
|
||||||
|
vars: [DOCKER_IMAGE]
|
||||||
|
dir: '/home/av/projects/private/pet-project-server'
|
||||||
|
cmd: ansible-playbook -i production.yml playbook-app-homepage.yml --extra-vars 'homepage_web_image={{.DOCKER_IMAGE}}'
|
@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Homepage\HtmlPrettierBundle\HtmlPrettierBundle;
|
use Homepage\HtmlPrettierBundle\HtmlPrettierBundle;
|
||||||
use Homepage\SiteMapBundle\SiteMapBundle;
|
use Homepage\SiteMapBundle\SiteMapBundle;
|
||||||
use Homepage\TwigExtensionBundle\TwigExtensionBundle;
|
use Homepage\TwigExtensionBundle\TwigExtensionBundle;
|
||||||
|
@ -5,16 +5,6 @@ sculpin:
|
|||||||
sculpin_content_types:
|
sculpin_content_types:
|
||||||
posts:
|
posts:
|
||||||
enabled: false
|
enabled: false
|
||||||
albums:
|
|
||||||
type: path
|
|
||||||
path: _albums
|
|
||||||
singular_name: album
|
|
||||||
layout: internal
|
|
||||||
permalink: albums/:basename/
|
|
||||||
publish_drafts: false
|
|
||||||
enabled: true
|
|
||||||
taxonomies:
|
|
||||||
- tags
|
|
||||||
articles:
|
articles:
|
||||||
type: path
|
type: path
|
||||||
path: _articles
|
path: _articles
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Homepage\HtmlPrettierBundle\DependencyInjection;
|
namespace Homepage\HtmlPrettierBundle\DependencyInjection;
|
||||||
|
|
||||||
use Symfony\Component\Config\FileLocator;
|
use Symfony\Component\Config\FileLocator;
|
||||||
@ -9,9 +11,6 @@ use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
|||||||
|
|
||||||
class HtmlPrettierExtension extends Extension
|
class HtmlPrettierExtension extends Extension
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function load(array $configs, ContainerBuilder $container)
|
public function load(array $configs, ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
||||||
|
@ -1,20 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Homepage\HtmlPrettierBundle;
|
namespace Homepage\HtmlPrettierBundle;
|
||||||
|
|
||||||
use Generator;
|
|
||||||
use Sculpin\Core\Event\SourceSetEvent;
|
use Sculpin\Core\Event\SourceSetEvent;
|
||||||
use Sculpin\Core\Sculpin;
|
use Sculpin\Core\Sculpin;
|
||||||
use Sculpin\Core\Source\SourceInterface;
|
use Sculpin\Core\Source\SourceInterface;
|
||||||
use Sculpin\Core\Source\SourceSet;
|
use Sculpin\Core\Source\SourceSet;
|
||||||
use function strlen;
|
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
|
|
||||||
class HtmlPrettier implements EventSubscriberInterface
|
class HtmlPrettier implements EventSubscriberInterface
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public static function getSubscribedEvents(): array
|
public static function getSubscribedEvents(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@ -43,7 +40,7 @@ class HtmlPrettier implements EventSubscriberInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function filterSource(SourceSet $sourceSet): Generator
|
private function filterSource(SourceSet $sourceSet): \Generator
|
||||||
{
|
{
|
||||||
/** @var SourceInterface $source */
|
/** @var SourceInterface $source */
|
||||||
foreach ($sourceSet->allSources() as $source) {
|
foreach ($sourceSet->allSources() as $source) {
|
||||||
@ -61,7 +58,7 @@ class HtmlPrettier implements EventSubscriberInterface
|
|||||||
|
|
||||||
private function endsWith($haystack, $needle): bool
|
private function endsWith($haystack, $needle): bool
|
||||||
{
|
{
|
||||||
$length = strlen($needle);
|
$length = \strlen($needle);
|
||||||
|
|
||||||
return $length === 0 || (substr($haystack, -$length) === $needle);
|
return $length === 0 || (substr($haystack, -$length) === $needle);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Homepage\HtmlPrettierBundle;
|
namespace Homepage\HtmlPrettierBundle;
|
||||||
|
|
||||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Homepage\SiteMapBundle\DependencyInjection;
|
namespace Homepage\SiteMapBundle\DependencyInjection;
|
||||||
|
|
||||||
use Symfony\Component\Config\FileLocator;
|
use Symfony\Component\Config\FileLocator;
|
||||||
@ -9,9 +11,6 @@ use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
|||||||
|
|
||||||
class SiteMapExtension extends Extension
|
class SiteMapExtension extends Extension
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function load(array $configs, ContainerBuilder $container)
|
public function load(array $configs, ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Homepage\SiteMapBundle;
|
namespace Homepage\SiteMapBundle;
|
||||||
|
|
||||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Homepage\SiteMapBundle;
|
namespace Homepage\SiteMapBundle;
|
||||||
|
|
||||||
use Sculpin\Core\DataProvider\DataProviderInterface;
|
use Sculpin\Core\DataProvider\DataProviderInterface;
|
||||||
@ -21,9 +23,6 @@ class SiteMapGenerator implements DataProviderInterface, EventSubscriberInterfac
|
|||||||
*/
|
*/
|
||||||
private $sources;
|
private $sources;
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public static function getSubscribedEvents(): array
|
public static function getSubscribedEvents(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@ -86,7 +85,7 @@ class SiteMapGenerator implements DataProviderInterface, EventSubscriberInterfac
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($data['draft']) {
|
if (array_key_exists('draft', $data) && $data['draft']) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Homepage\TwigExtensionBundle\DependencyInjection;
|
namespace Homepage\TwigExtensionBundle\DependencyInjection;
|
||||||
|
|
||||||
use Symfony\Component\Config\FileLocator;
|
use Symfony\Component\Config\FileLocator;
|
||||||
@ -9,9 +11,6 @@ use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
|||||||
|
|
||||||
class TwigExtensionExtension extends Extension
|
class TwigExtensionExtension extends Extension
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function load(array $configs, ContainerBuilder $container)
|
public function load(array $configs, ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Homepage\TwigExtensionBundle;
|
namespace Homepage\TwigExtensionBundle;
|
||||||
|
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Homepage\TwigExtensionBundle;
|
namespace Homepage\TwigExtensionBundle;
|
||||||
|
|
||||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||||
|
@ -9,16 +9,20 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": "~7.3",
|
"php": "^8.0",
|
||||||
"ext-tidy": "*"
|
"ext-tidy": "*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"friendsofphp/php-cs-fixer": "^2.15",
|
|
||||||
"sculpin/sculpin": "^3.0"
|
"sculpin/sculpin": "^3.0"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Homepage\\": "bundle/"
|
"Homepage\\": "bundle/"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"allow-plugins": {
|
||||||
|
"sculpin/sculpin-theme-composer-plugin": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1974
composer.lock
generated
1974
composer.lock
generated
File diff suppressed because it is too large
Load Diff
49
deploy.php
49
deploy.php
@ -1,49 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Deployer;
|
|
||||||
|
|
||||||
require 'recipe/common.php';
|
|
||||||
|
|
||||||
host('vakhrushev.me')
|
|
||||||
->user('homepage')
|
|
||||||
->stage('production')
|
|
||||||
->set('deploy_path', '/var/www/homepage')
|
|
||||||
;
|
|
||||||
|
|
||||||
host('192.168.50.10')
|
|
||||||
->stage('test')
|
|
||||||
->user('homepage')
|
|
||||||
->set('deploy_path', '/var/www/homepage')
|
|
||||||
->addSshOption('UserKnownHostsFile', '/dev/null')
|
|
||||||
->addSshOption('StrictHostKeyChecking', 'no')
|
|
||||||
;
|
|
||||||
|
|
||||||
// Saved releases
|
|
||||||
set('keep_releases', 2);
|
|
||||||
|
|
||||||
// Excluded dirs for upload
|
|
||||||
set('upload_excluded_dirs', []);
|
|
||||||
|
|
||||||
// Upload app sources on remote host
|
|
||||||
task('upload', function () {
|
|
||||||
$excluded = array_map(function ($dir) {
|
|
||||||
return sprintf('--exclude "%s"', $dir);
|
|
||||||
}, get('upload_excluded_dirs'));
|
|
||||||
upload(__DIR__ . '/output_prod/', '{{release_path}}', [
|
|
||||||
'options' => $excluded,
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Deploy task
|
|
||||||
task('deploy', [
|
|
||||||
'deploy:info',
|
|
||||||
'deploy:prepare',
|
|
||||||
'deploy:lock',
|
|
||||||
'deploy:release',
|
|
||||||
'upload',
|
|
||||||
'deploy:symlink',
|
|
||||||
'deploy:unlock',
|
|
||||||
'cleanup',
|
|
||||||
]);
|
|
||||||
|
|
||||||
after('deploy', 'success');
|
|
@ -1,7 +0,0 @@
|
|||||||
FROM php:7.4.7-cli
|
|
||||||
|
|
||||||
COPY ./docker/provision.sh /opt/
|
|
||||||
|
|
||||||
RUN /opt/provision.sh
|
|
||||||
|
|
||||||
WORKDIR /srv/app
|
|
3
docker/Dockerfile.nginx.prod
Normal file
3
docker/Dockerfile.nginx.prod
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
FROM nginx:stable
|
||||||
|
|
||||||
|
COPY output_prod /usr/share/nginx/html
|
6
docker/node/Dockerfile
Normal file
6
docker/node/Dockerfile
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
FROM node:12
|
||||||
|
|
||||||
|
ENV npm_config_fund=false
|
||||||
|
ENV npm_config_update_notifier=false
|
||||||
|
|
||||||
|
WORKDIR /srv/app
|
29
docker/php/Dockerfile
Normal file
29
docker/php/Dockerfile
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
FROM php:8.1-cli
|
||||||
|
|
||||||
|
ENV COMPOSER_FUND=0
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
curl \
|
||||||
|
git \
|
||||||
|
gnupg \
|
||||||
|
gzip \
|
||||||
|
libtidy-dev \
|
||||||
|
rsync \
|
||||||
|
zip \
|
||||||
|
;
|
||||||
|
|
||||||
|
RUN docker-php-ext-install tidy \
|
||||||
|
&& docker-php-ext-enable tidy
|
||||||
|
|
||||||
|
# Composer and required tools
|
||||||
|
RUN curl -sLO https://getcomposer.org/download/2.8.4/composer.phar \
|
||||||
|
&& mv composer.phar /usr/local/bin/composer \
|
||||||
|
&& chmod +x /usr/local/bin/composer
|
||||||
|
|
||||||
|
|
||||||
|
# PHP-CS-Fixer
|
||||||
|
RUN curl -sLO https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/download/v3.65.0/php-cs-fixer.phar \
|
||||||
|
&& mv php-cs-fixer.phar /usr/local/bin/php-cs-fixer \
|
||||||
|
&& chmod +x /usr/local/bin/php-cs-fixer
|
||||||
|
|
||||||
|
WORKDIR /srv/app
|
@ -1,29 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
|
|
||||||
set -eux
|
|
||||||
|
|
||||||
apt-get update && apt-get install -y \
|
|
||||||
curl \
|
|
||||||
git \
|
|
||||||
gnupg \
|
|
||||||
gzip \
|
|
||||||
libtidy-dev \
|
|
||||||
rsync \
|
|
||||||
zip \
|
|
||||||
;
|
|
||||||
|
|
||||||
docker-php-ext-install tidy \
|
|
||||||
&& docker-php-ext-enable tidy
|
|
||||||
|
|
||||||
# Project folder
|
|
||||||
mkdir -p /srv/app
|
|
||||||
|
|
||||||
# Composer and required tools
|
|
||||||
curl -sLO https://getcomposer.org/download/1.10.8/composer.phar \
|
|
||||||
&& mv composer.phar /usr/local/bin/composer \
|
|
||||||
&& chmod +x /usr/local/bin/composer
|
|
||||||
|
|
||||||
# Deployer
|
|
||||||
curl -sLO https://deployer.org/releases/v6.8.0/deployer.phar \
|
|
||||||
&& mv deployer.phar /usr/local/bin/dep \
|
|
||||||
&& chmod +x /usr/local/bin/dep
|
|
5247
npm-shrinkwrap.json → package-lock.json
generated
5247
npm-shrinkwrap.json → package-lock.json
generated
File diff suppressed because it is too large
Load Diff
28
package.json
28
package.json
@ -6,27 +6,27 @@
|
|||||||
"description": "Homepage",
|
"description": "Homepage",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@anwinged/predictor": "^0.2.1",
|
"@anwinged/predictor": "^0.2.1",
|
||||||
"@babel/core": "^7.10.3",
|
"@babel/core": "^7.14.6",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.10.1",
|
"@babel/plugin-proposal-class-properties": "^7.14.5",
|
||||||
"@babel/plugin-transform-runtime": "^7.10.3",
|
"@babel/plugin-transform-runtime": "^7.14.5",
|
||||||
"@babel/preset-env": "^7.10.3",
|
"@babel/preset-env": "^7.14.7",
|
||||||
"@babel/runtime": "^7.10.3",
|
"@babel/runtime": "^7.14.6",
|
||||||
"autoprefixer": "^9.8.4",
|
"autoprefixer": "^9.8.6",
|
||||||
"babel-loader": "^8.1.0",
|
"babel-loader": "^8.2.2",
|
||||||
"css-loader": "^2.1.1",
|
"css-loader": "^2.1.1",
|
||||||
"glob": "^7.1.6",
|
"glob": "^7.1.7",
|
||||||
"mini-css-extract-plugin": "^0.6.0",
|
"mini-css-extract-plugin": "^0.6.0",
|
||||||
"node-sass": "^4.14.1",
|
"node-sass": "^4.14.1",
|
||||||
"postcss-loader": "^3.0.0",
|
"postcss-loader": "^3.0.0",
|
||||||
"prettier": "^1.19.1",
|
"prettier": "^1.19.1",
|
||||||
"sass-loader": "^7.3.1",
|
"sass-loader": "^7.3.1",
|
||||||
"style-loader": "^0.23.1",
|
"style-loader": "^0.23.1",
|
||||||
"underscore": "^1.10.2",
|
"underscore": "^1.13.1",
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.14",
|
||||||
"vue-loader": "^15.9.3",
|
"vue-loader": "^15.9.7",
|
||||||
"vue-style-loader": "^4.1.2",
|
"vue-style-loader": "^4.1.3",
|
||||||
"vue-template-compiler": "^2.6.11",
|
"vue-template-compiler": "^2.6.14",
|
||||||
"webpack": "^4.43.0",
|
"webpack": "^4.46.0",
|
||||||
"webpack-cli": "^3.3.12"
|
"webpack-cli": "^3.3.12"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
---
|
|
||||||
title: Белое море - 2011, 2013 года
|
|
||||||
description: Фотографии с поездок на Белое море в самую короткую ночь в году
|
|
||||||
---
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://img-fotki.yandex.ru/get/9222/46045840.28/0_98765_7fc8895f_XXXL.jpg">
|
|
||||||
<img src="https://img-fotki.yandex.ru/get/9222/46045840.28/0_98765_7fc8895f_XL.jpg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://img-fotki.yandex.ru/get/9312/46045840.28/0_98748_e6ab013e_XXXL.jpg">
|
|
||||||
<img src="https://img-fotki.yandex.ru/get/9312/46045840.28/0_98748_e6ab013e_XL.jpg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://img-fotki.yandex.ru/get/9231/46045840.28/0_98749_b4fb46a9_XXXL.jpg">
|
|
||||||
<img src="https://img-fotki.yandex.ru/get/9231/46045840.28/0_98749_b4fb46a9_XL.jpg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://img-fotki.yandex.ru/get/9305/46045840.28/0_9874a_afbf859b_XXXL.jpg">
|
|
||||||
<img src="https://img-fotki.yandex.ru/get/9305/46045840.28/0_9874a_afbf859b_XL.jpg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://img-fotki.yandex.ru/get/9171/46045840.28/0_98747_7d4ca4c9_XXXL.jpg">
|
|
||||||
<img src="https://img-fotki.yandex.ru/get/9171/46045840.28/0_98747_7d4ca4c9_XL.jpg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://img-fotki.yandex.ru/get/9217/46045840.28/0_9874b_e42da6b4_XXXL.jpg">
|
|
||||||
<img src="https://img-fotki.yandex.ru/get/9217/46045840.28/0_9874b_e42da6b4_XL.jpg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://img-fotki.yandex.ru/get/9228/46045840.28/0_9874f_9af8aa8f_XXXL.jpg">
|
|
||||||
<img src="https://img-fotki.yandex.ru/get/9228/46045840.28/0_9874f_9af8aa8f_XL.jpg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://img-fotki.yandex.ru/get/9301/46045840.28/0_9875a_d96bfbd7_XXXL.jpg">
|
|
||||||
<img src="https://img-fotki.yandex.ru/get/9301/46045840.28/0_9875a_d96bfbd7_XL.jpg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://img-fotki.yandex.ru/get/9324/46045840.28/0_98756_13fee051_XXXL.jpg">
|
|
||||||
<img src="https://img-fotki.yandex.ru/get/9324/46045840.28/0_98756_13fee051_XL.jpg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
@ -1,67 +0,0 @@
|
|||||||
---
|
|
||||||
title: Зимний туман
|
|
||||||
description: Фотографи набережной Седова, сделанные в туманный зимний день
|
|
||||||
---
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://img-fotki.yandex.ru/get/5821/46045840.24/0_6f263_3e1f58f6_XXXL.jpg">
|
|
||||||
<img src="https://img-fotki.yandex.ru/get/5821/46045840.24/0_6f263_3e1f58f6_XL.jpg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://img-fotki.yandex.ru/get/4419/46045840.24/0_6f25c_397758c6_XXXL.jpg">
|
|
||||||
<img src="https://img-fotki.yandex.ru/get/4419/46045840.24/0_6f25c_397758c6_XL.jpg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://img-fotki.yandex.ru/get/5313/46045840.24/0_6f256_e62604eb_XXXL.jpg">
|
|
||||||
<img src="https://img-fotki.yandex.ru/get/5313/46045840.24/0_6f256_e62604eb_XL.jpg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://img-fotki.yandex.ru/get/5313/46045840.24/0_6f257_5eaf214c_XXXL.jpg">
|
|
||||||
<img src="https://img-fotki.yandex.ru/get/5313/46045840.24/0_6f257_5eaf214c_XL.jpg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://img-fotki.yandex.ru/get/4614/46045840.24/0_6f259_3caa7abe_XXXL.jpg">
|
|
||||||
<img src="https://img-fotki.yandex.ru/get/4614/46045840.24/0_6f259_3caa7abe_XXXL.jpg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a class="photo-link" href="https://img-fotki.yandex.ru/get/4419/46045840.24/0_6f25a_4f1fa9ef_XXXL.jpg">
|
|
||||||
<img class="photo-image" src="https://img-fotki.yandex.ru/get/4419/46045840.24/0_6f25a_4f1fa9ef_XL.jpg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://img-fotki.yandex.ru/get/4614/46045840.24/0_6f25b_8a14fb24_XXXL.jpg">
|
|
||||||
<img src="https://img-fotki.yandex.ru/get/4614/46045840.24/0_6f25b_8a14fb24_XL.jpg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://img-fotki.yandex.ru/get/4419/46045840.24/0_6f25d_b4fd58d3_XXXL.jpg">
|
|
||||||
<img src="https://img-fotki.yandex.ru/get/4419/46045840.24/0_6f25d_b4fd58d3_XL.jpg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://img-fotki.yandex.ru/get/4614/46045840.24/0_6f25f_b60733cd_XXXL.jpg">
|
|
||||||
<img src="https://img-fotki.yandex.ru/get/4614/46045840.24/0_6f25f_b60733cd_XL.jpg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://img-fotki.yandex.ru/get/4418/46045840.24/0_6f255_c458f69c_XXXL.jpg">
|
|
||||||
<img src="https://img-fotki.yandex.ru/get/4418/46045840.24/0_6f255_c458f69c_XL.jpg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
177
source/_articles/2020-11-08-nullable-fields.md
Normal file
177
source/_articles/2020-11-08-nullable-fields.md
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
---
|
||||||
|
title: Организация доступа к nullable полям класса
|
||||||
|
description: Заметка о том, лучше организовать доступ к полям класса, которые могут содержать значение null
|
||||||
|
keywords: [чистый код, php, null, поля класса]
|
||||||
|
---
|
||||||
|
|
||||||
|
Нередкая ситуация, когда в классе есть поле, которое может содержать `null`.
|
||||||
|
|
||||||
|
```php
|
||||||
|
class User
|
||||||
|
{
|
||||||
|
private Email $email;
|
||||||
|
private ?string $name;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Пользователь может указать имя, а может и не указывать,
|
||||||
|
ограничившись только почтовым адресом.
|
||||||
|
|
||||||
|
А далее мы пишем код, которые работает с полем имени.
|
||||||
|
|
||||||
|
```php
|
||||||
|
class User
|
||||||
|
{
|
||||||
|
private ?string $name;
|
||||||
|
|
||||||
|
public function hasName(): bool
|
||||||
|
{
|
||||||
|
return $this->name !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): ?string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setName(?string $name): void
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
И использование этого кода:
|
||||||
|
|
||||||
|
```php
|
||||||
|
/** @var User $user */
|
||||||
|
|
||||||
|
if ($user->hasName()) {
|
||||||
|
do_something_with_name($user->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
function do_something_with_name(string $name) {}
|
||||||
|
```
|
||||||
|
|
||||||
|
Выглядит хорошо.
|
||||||
|
Сначала убедились, что имя установлено, а потом использовали его.
|
||||||
|
|
||||||
|
Но статический анализатор нам обязательно припомнит, что мы пытаемся передать
|
||||||
|
в функцию `do_something_with_name` значение типа `string|null`, хотя функция
|
||||||
|
ожидает значение типа `string`.
|
||||||
|
И получается дурацкая ситуация, что формально мы должны дописать
|
||||||
|
еще одну проверку.
|
||||||
|
|
||||||
|
```php
|
||||||
|
/** @var User $user */
|
||||||
|
|
||||||
|
if ($user->hasName()) {
|
||||||
|
$name = $user->getName();
|
||||||
|
if ($name !== null) {
|
||||||
|
do_something_with_name($name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function do_something_with_name(string $name) {}
|
||||||
|
```
|
||||||
|
|
||||||
|
Статический анализатор наш друг, он помогает находить ошибки и несоответствия
|
||||||
|
в коде.
|
||||||
|
И здесь он нашел такое формальное несоответствие типов.
|
||||||
|
|
||||||
|
Статический анализатор прав, а мы, как проектировщики интерфейса, не правы.
|
||||||
|
На самом деле мы смешали два подхода, когда описывали методы в нашем классе:
|
||||||
|
|
||||||
|
1. Получить и проверить
|
||||||
|
2. Проверить и получить
|
||||||
|
|
||||||
|
## Получить и проверить
|
||||||
|
|
||||||
|
И сразу начнем с примера использования.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$name = $user->getName();
|
||||||
|
if ($name !== null) {
|
||||||
|
do_something_with_name($name);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Сначала мы получаем значение поля, а потом проверяем, соответствует ли это
|
||||||
|
значение нашим требованиям. Класс при этом будет построен вот так:
|
||||||
|
|
||||||
|
```php
|
||||||
|
class User
|
||||||
|
{
|
||||||
|
private ?string $name;
|
||||||
|
|
||||||
|
public function getName(): ?string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setName(?string $name): void
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Заметьте, здесь уже нет метода `hasName()`, потому что этот метод перестал быть
|
||||||
|
нужным. Его роль исполняет метод `getName()`.
|
||||||
|
|
||||||
|
## Проверить и получить
|
||||||
|
|
||||||
|
Второй подход: сначала проверяем значение, а потом работаем с ним:
|
||||||
|
|
||||||
|
```php
|
||||||
|
if ($user->hasName()) {
|
||||||
|
do_something_with_name($user->getName());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Структура класса:
|
||||||
|
|
||||||
|
```php
|
||||||
|
class User
|
||||||
|
{
|
||||||
|
private ?string $name;
|
||||||
|
|
||||||
|
public function hasName(): bool
|
||||||
|
{
|
||||||
|
return $this->name !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
if ($this->name === null) {
|
||||||
|
throw new \LogicException('Name is not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setName(?string $name): void
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Смотрите отличия.
|
||||||
|
Метод `hasName()` остается.
|
||||||
|
А вот метод `getName()` теперь возвращает значение типа `string`.
|
||||||
|
Он выбросит исключение, если мы попытаемся получить значение,
|
||||||
|
которое не установлено.
|
||||||
|
|
||||||
|
## Использование
|
||||||
|
|
||||||
|
Теперь встает вопрос, когда и какой подход следует использовать.
|
||||||
|
|
||||||
|
- Если ситуация, когда поле не установлено, скорее исключительная, нежели
|
||||||
|
обычная, то можно использовать второй подход, а проверку опустить.
|
||||||
|
Исключение в методе `getName()` позволит обнаружить странное поведение.
|
||||||
|
- Если в пустом поле нет ничего не обычного, то подход "получить и проверить"
|
||||||
|
будет удобнее, все равно нужно делать проверку.
|
||||||
|
|
||||||
|
В любом случае, нужно смотреть на уместность того или иного подхода в каждом
|
||||||
|
случае, и не использовать их одновременно.
|
@ -1,78 +0,0 @@
|
|||||||
<template>
|
|
||||||
<section class="app">
|
|
||||||
<p class="fact-index">Факт {{ factIndex }}</p>
|
|
||||||
<p class="note">{{ fact }}</p>
|
|
||||||
<button @click.prevent="next" class="button-next">
|
|
||||||
Узнать чуть лучше
|
|
||||||
</button>
|
|
||||||
</section>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import _ from 'underscore';
|
|
||||||
|
|
||||||
const NOTES = [
|
|
||||||
'Люблю фильм "Три идиота".',
|
|
||||||
'Люблю кататься на велосипеде.',
|
|
||||||
'Люблю читать фантастические книги.',
|
|
||||||
'Люблю шоколад.',
|
|
||||||
'На день рождения ко мне можно прийти без подарка.',
|
|
||||||
'Не люблю пьяных людей.',
|
|
||||||
'Предпочитаю ходить в кино на 2D-сеансы.',
|
|
||||||
'Проехал на велосипеде 200 километров за день.',
|
|
||||||
'Работаю программистом.',
|
|
||||||
'Хотел бы побывать в горах.',
|
|
||||||
];
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
notes: NOTES,
|
|
||||||
shown: [],
|
|
||||||
fact: '',
|
|
||||||
factIndex: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.pick();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
next() {
|
|
||||||
this.pick();
|
|
||||||
},
|
|
||||||
pick() {
|
|
||||||
let available = _.difference(this.notes, this.shown);
|
|
||||||
if (_.size(available) === 0) {
|
|
||||||
available = this.notes;
|
|
||||||
this.shown = [];
|
|
||||||
}
|
|
||||||
const fact = _.sample(available);
|
|
||||||
this.shown.push(fact);
|
|
||||||
this.fact = fact;
|
|
||||||
this.factIndex = _.indexOf(NOTES, fact) + 1;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
@import '../components/button';
|
|
||||||
.app {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fact-index {
|
|
||||||
margin-top: 3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note {
|
|
||||||
display: block;
|
|
||||||
font-size: 160%;
|
|
||||||
margin: 0 auto 2em;
|
|
||||||
min-height: 3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-next {
|
|
||||||
@extend %button;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,7 +0,0 @@
|
|||||||
import Vue from 'vue';
|
|
||||||
import About from './about.vue';
|
|
||||||
|
|
||||||
new Vue({
|
|
||||||
el: '#app',
|
|
||||||
render: h => h(About),
|
|
||||||
});
|
|
@ -1,19 +0,0 @@
|
|||||||
---
|
|
||||||
layout: internal
|
|
||||||
title: Обо мне
|
|
||||||
description: Несколько фактов об авторе
|
|
||||||
---
|
|
||||||
|
|
||||||
{% block js %}
|
|
||||||
{{ parent() }}
|
|
||||||
<script async src="{{ hashed_asset('/static/about.js') }}"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block css %}
|
|
||||||
{{ parent() }}
|
|
||||||
<link rel="stylesheet" href="{{ hashed_asset('/static/about.css') }}">
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div id="app"></div>
|
|
||||||
{% endblock %}
|
|
@ -1,25 +0,0 @@
|
|||||||
---
|
|
||||||
layout: internal
|
|
||||||
title: Фотоальбомы
|
|
||||||
description: Фотоальбомы
|
|
||||||
use: [albums]
|
|
||||||
---
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<h1>{{ page.title }}</h1>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
{% for album in data.albums %}
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
<a href="{{ album.url }}">{{ album.title }}</a>
|
|
||||||
{% if album.description %}
|
|
||||||
<br>
|
|
||||||
{{ album.description }}
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
@ -25,7 +25,6 @@ use: [articles]
|
|||||||
Работаю в <a href="https://playkot.com/" target="_blank">Playkot</a>.
|
Работаю в <a href="https://playkot.com/" target="_blank">Playkot</a>.
|
||||||
Пишу на PHP.
|
Пишу на PHP.
|
||||||
Разбираюсь в back-end, экспериментирую с front-end, интересуюсь функциональным программированием.
|
Разбираюсь в back-end, экспериментирую с front-end, интересуюсь функциональным программированием.
|
||||||
Иногда <a href="/albums/">фотографирую</a>.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<span class="hr-line"></span>
|
<span class="hr-line"></span>
|
||||||
@ -41,23 +40,8 @@ use: [articles]
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="social__item">
|
<li class="social__item">
|
||||||
<a class="social__link" href="https://github.com/anwinged" target="_blank" title="Код на Гитхабе">
|
<a class="social__link" href="https://git.vakhrushev.me" target="_blank" title="Код на Гитхабе">
|
||||||
<i class="fab fa-github"></i>
|
<i class="fab fa-git-square"></i>
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="social__item">
|
|
||||||
<a class="social__link" href="https://www.linkedin.com/in/anton-vakhrushev" target="_blank" title="Профиль на Линкедин">
|
|
||||||
<i class="fab fa-linkedin"></i>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="social__item">
|
|
||||||
<a class="social__link" href="https://instagram.com/anwinged" target="_blank" title="Фотографии в Инстаграме">
|
|
||||||
<i class="fab fa-instagram"></i>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="social__item">
|
|
||||||
<a class="social__link" href="/about/" target="_blank" title="Об авторе">
|
|
||||||
<i class="fas fa-ghost"></i>
|
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="social__item">
|
<li class="social__item">
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
source .env
|
|
||||||
|
|
||||||
docker build \
|
|
||||||
--file docker/Dockerfile \
|
|
||||||
--tag "${PHP_IMAGE}" \
|
|
||||||
"$PWD"
|
|
@ -1,19 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
source .env
|
|
||||||
|
|
||||||
mkdir -p var/docker-cache/.composer
|
|
||||||
|
|
||||||
docker run \
|
|
||||||
--rm \
|
|
||||||
--interactive \
|
|
||||||
--tty \
|
|
||||||
--user "$UID:$(id -g)" \
|
|
||||||
--volume /etc/passwd:/etc/passwd:ro \
|
|
||||||
--volume /etc/group:/etc/group:ro \
|
|
||||||
--volume "$PWD:/srv/app" \
|
|
||||||
--volume "$HOME:$HOME" \
|
|
||||||
--volume "$PWD/var/docker-cache/.composer:/tmp/.composer" \
|
|
||||||
--env COMPOSER_HOME=/tmp/.composer \
|
|
||||||
"${PHP_IMAGE}" \
|
|
||||||
composer "$@"
|
|
15
tools/dep
15
tools/dep
@ -1,15 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
source .env
|
|
||||||
|
|
||||||
docker run \
|
|
||||||
--rm \
|
|
||||||
--interactive \
|
|
||||||
--tty \
|
|
||||||
--user "$UID:$(id -g)" \
|
|
||||||
--volume /etc/passwd:/etc/passwd:ro \
|
|
||||||
--volume /etc/group:/etc/group:ro \
|
|
||||||
--volume "$HOME:$HOME" \
|
|
||||||
--volume "$PWD:/srv/app" \
|
|
||||||
"${PHP_IMAGE}" \
|
|
||||||
dep "$@"
|
|
@ -1,5 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -eux
|
|
||||||
|
|
||||||
touch "source/_articles/$(date +'%Y-%m-%d')-$1.md"
|
|
20
tools/npm
20
tools/npm
@ -1,20 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
source .env
|
|
||||||
|
|
||||||
mkdir -p var/docker-cache/.npm
|
|
||||||
|
|
||||||
docker run \
|
|
||||||
--rm \
|
|
||||||
--interactive \
|
|
||||||
--tty \
|
|
||||||
--user "$UID:$(id -g)" \
|
|
||||||
--volume /etc/passwd:/etc/passwd:ro \
|
|
||||||
--volume /etc/group:/etc/group:ro \
|
|
||||||
--volume "$PWD:/srv/app" \
|
|
||||||
--volume "$HOME:$HOME" \
|
|
||||||
--volume "$PWD/var/docker-cache/.npm:/tmp/.npm" \
|
|
||||||
--env npm_config_cache=/tmp/.npm \
|
|
||||||
--workdir /srv/app \
|
|
||||||
"${NODE_IMAGE}" \
|
|
||||||
npm "$@"
|
|
@ -1,13 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
source .env
|
|
||||||
|
|
||||||
docker run \
|
|
||||||
--rm \
|
|
||||||
--interactive \
|
|
||||||
--tty \
|
|
||||||
--init \
|
|
||||||
--user "$UID:$(id -g)" \
|
|
||||||
--volume="$PWD:/srv/app" \
|
|
||||||
"${PHP_IMAGE}" \
|
|
||||||
./vendor/bin/php-cs-fixer "$@"
|
|
@ -1,15 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
source .env
|
|
||||||
|
|
||||||
docker run \
|
|
||||||
--rm \
|
|
||||||
--interactive \
|
|
||||||
--tty \
|
|
||||||
--init \
|
|
||||||
--user "$UID:$(id -g)" \
|
|
||||||
--volume="$PWD:/srv/app" \
|
|
||||||
--expose=8000 \
|
|
||||||
--publish=8000:8000 \
|
|
||||||
"${PHP_IMAGE}" \
|
|
||||||
./vendor/bin/sculpin "$@"
|
|
Reference in New Issue
Block a user