Jump to content

NotionCommotion

Members
  • Posts

    2,437
  • Joined

  • Last visited

  • Days Won

    10

Everything posted by NotionCommotion

  1. Thanks maxxd, I'll checkout the facade object/pattern, but likely won't use it for this use. Thanks kicken, In hindsight, I agree chaining doesn't bring much value for this case. "If it did" (which it doesn't), guess I could just add another traditional method which would call the static method and directly return $this (but I won't).
  2. Is it possible to return the instance of an object when a static method is called on it? For instance, (new SomeClass)->echo() returns $this and SomeClass::echo() return a newly created object? Thinking I could do so by making properties also static and then creating the new object and populating it (albeit it will be a clone and not the same object). Thanks <?php class SomeClass { private $services = []; private $paramaters = []; public function Foo():self { echo(__METHOD__); return $this; } public static function echo(string $msg, array $data=[], bool $syslog = false):self|static { echo($msg.($data?(': data: '.json_encode($data)):'').PHP_EOL); return $syslog?self::syslog($msg, $data):$this->getSelfOrStatic(); } public static function syslog(string $msg, array $data=[], bool $echo = false):self|static { syslog(LOG_INFO, $msg.($data?(': data: '.json_encode($data)):'')); return $echo?self::echo($msg, $data):$this->getSelfOrStatic(); } private static function getSelfOrStatic():self|static { return isset($this)?$this:new static(); } }
  3. Thanks maxxd, If I go through your configuration, would you be willing to answer some specific questions in order to confirm my understanding? If so, think it is best to fully understand the Dockerfile before moving to the docker-compose.yml file, or the other way around?
  4. Super/sub type DB modelling provides a more structured approach for inheritance and centralizes the conditional statement logic and IMO provides polymorphism, however, be careful as if taken too far, will likely paint you into a corner that you didn't want to be in, so use sparingly when the benefits outweigh the risks.
  5. I mostly figured out the SQL error. Appears this was being called by Dockerfile which added a table called user which I had been using, and I didn't realize it was called when the image was being created. Maybe got some technicalities wrong, but changing it got rid of the SQL errors. # api/docker/php/docker-entrypoint.sh #!/bin/sh # bla bla bla... if [ "$( find ./migrations -iname '*.php' -print -quit )" ]; then php bin/console doctrine:migrations:migrate --no-interaction fi if [ "$APP_ENV" != 'prod' ]; then echo "Load fixtures" bin/console hautelook:fixtures:load --no-interaction fi fi fi exec docker-php-entrypoint "$@" Still don't understand the composer part and the not found classes. For instance, I see stof/doctrine-extensions-bundle (v1.7.1) first being installed, but then I get Uncaught Error: Class "Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle" not found error. The only place I see that class being used is in bundles.php, and suppose I could comment it out like I did for libphonenumber\PhoneNumberUtil, but then suspect some other issue will pop up. Not sure where to go next on this... EDIT. Maybe I got lucky! Commented it out and at least PHP is not crashing. Still need to better understand how all this stuff works... facdocs-consumer-1 exited with code 255 facdocs-php-1 | - Installing behat/transliterator (v1.5.0): Extracting archive facdocs-php-1 | - Installing brick/money (0.8.0): Extracting archive facdocs-php-1 | - Installing doctrine/doctrine-fixtures-bundle (3.4.2): Extracting archive facdocs-php-1 | - Installing fig/link-util (1.2.0): Extracting archive facdocs-php-1 | - Installing gesdinet/jwt-refresh-token-bundle (v1.1.1): Extracting archive facdocs-php-1 | - Installing giggsey/locale (2.3): Extracting archive facdocs-php-1 | - Installing laminas/laminas-code (4.8.0): Extracting archive facdocs-php-1 | - Installing symfony/translation (v6.1.11): Extracting archive facdocs-php-1 | - Installing nesbot/carbon (2.66.0): Extracting archive facdocs-php-1 | - Installing nilportugues/sql-query-formatter (v1.2.2): Extracting archive facdocs-php-1 | - Installing notion-commotion/attribute-validator (1.01): Extracting archive facdocs-php-1 | - Installing notion-commotion/attribute-validator-command (1.06): Extracting archive facdocs-php-1 | - Installing symfony/intl (v6.1.11): Extracting archive facdocs-php-1 | - Installing giggsey/libphonenumber-for-php (8.13.6): Extracting archive facdocs-php-1 | - Installing odolbeau/phone-number-bundle (v3.9.1): Extracting archive facdocs-php-1 | - Installing phpstan/phpdoc-parser (1.16.1): Extracting archive facdocs-php-1 | - Installing sebastian/version (3.0.2): Extracting archive facdocs-php-1 | - Installing sebastian/type (3.2.1): Extracting archive facdocs-php-1 | - Installing sebastian/resource-operations (3.0.3): Extracting archive facdocs-php-1 | - Installing sebastian/object-reflector (2.0.4): Extracting archive facdocs-php-1 | - Installing sebastian/object-enumerator (4.0.4): Extracting archive facdocs-php-1 | - Installing sebastian/global-state (5.0.5): Extracting archive facdocs-php-1 | - Installing sebastian/environment (5.1.5): Extracting archive facdocs-php-1 | - Installing sebastian/code-unit (1.0.8): Extracting archive facdocs-php-1 | - Installing sebastian/cli-parser (1.0.1): Extracting archive facdocs-php-1 | - Installing phpunit/php-timer (5.0.3): Extracting archive facdocs-php-1 | - Installing phpunit/php-text-template (2.0.4): Extracting archive facdocs-php-1 | - Installing phpunit/php-invoker (3.1.1): Extracting archive facdocs-php-1 | - Installing phpunit/php-file-iterator (3.0.6): Extracting archive facdocs-php-1 | - Installing theseer/tokenizer (1.2.1): Extracting archive facdocs-php-1 | - Installing sebastian/lines-of-code (1.0.3): Extracting archive facdocs-php-1 | - Installing sebastian/complexity (2.0.2): Extracting archive facdocs-php-1 | - Installing sebastian/code-unit-reverse-lookup (2.0.3): Extracting archive facdocs-php-1 | - Installing phpunit/php-code-coverage (9.2.24): Extracting archive facdocs-php-1 | - Installing phar-io/version (3.2.1): Extracting archive facdocs-php-1 | - Installing phar-io/manifest (2.0.3): Extracting archive facdocs-php-1 | - Installing phpunit/phpunit (9.6.3): Extracting archive facdocs-php-1 | - Installing phpstan/phpstan (1.10.2): Extracting archive facdocs-php-1 | - Installing rector/rector (0.15.18): Extracting archive facdocs-php-1 | - Installing gedmo/doctrine-extensions (v3.11.1): Extracting archive facdocs-php-1 | - Installing stof/doctrine-extensions-bundle (v1.7.1): Extracting archive facdocs-php-1 | - Installing symfony/polyfill-intl-idn (v1.27.0): Extracting archive facdocs-php-1 | - Installing symfony/mime (v6.1.11): Extracting archive facdocs-php-1 | - Installing egulias/email-validator (4.0.1): Extracting archive facdocs-php-1 | - Installing symfony/mailer (v6.1.11): Extracting archive facdocs-php-1 | - Installing symfony/polyfill-intl-icu (v1.27.0): Extracting archive facdocs-php-1 | - Installing friendsofphp/proxy-manager-lts (v1.0.14): Extracting archive facdocs-php-1 | - Installing symfony/proxy-manager-bridge (v6.1.11): Extracting archive facdocs-php-1 | - Installing symfony/polyfill-uuid (v1.27.0): Extracting archive facdocs-php-1 | - Installing symfony/uid (v6.1.11): Extracting archive facdocs-php-1 | - Installing symfony/templating (v6.1.11): Extracting archive facdocs-php-1 | - Installing symfony/form (v6.1.11): Extracting archive facdocs-php-1 | - Installing moneyphp/money (v3.3.3): Extracting archive facdocs-php-1 | - Installing tbbc/money-bundle (5.0.1): Extracting archive facdocs-php-1 | Package webmozart/path-util is abandoned, you should avoid using it. Use symfony/filesystem instead. facdocs-php-1 | Generating optimized autoload files facdocs-consumer-1 | 2023-02-24T18:05:33+00:00 [critical] Uncaught Error: Class "Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle" not found facdocs-consumer-1 | Symfony\Component\ErrorHandler\Error\ClassNotFoundError {#69 facdocs-consumer-1 | #message: """ facdocs-consumer-1 | Attempted to load class "StofDoctrineExtensionsBundle" from namespace "Stof\DoctrineExtensionsBundle".\n facdocs-consumer-1 | Did you forget a "use" statement for another namespace? facdocs-consumer-1 | """ facdocs-consumer-1 | #code: 0 facdocs-consumer-1 | #file: "./vendor/symfony/framework-bundle/Kernel/MicroKernelTrait.php" facdocs-consumer-1 | #line: 135 facdocs-consumer-1 | trace: { facdocs-consumer-1 | ./vendor/symfony/framework-bundle/Kernel/MicroKernelTrait.php:135 { …} facdocs-consumer-1 | ./vendor/symfony/http-kernel/Kernel.php:382 { …} facdocs-consumer-1 | ./vendor/symfony/http-kernel/Kernel.php:766 { …} facdocs-consumer-1 | ./vendor/symfony/http-kernel/Kernel.php:128 { …} facdocs-consumer-1 | ./vendor/symfony/framework-bundle/Console/Application.php:166 { …} facdocs-consumer-1 | ./vendor/symfony/framework-bundle/Console/Application.php:72 { …} facdocs-consumer-1 | ./vendor/symfony/console/Application.php:171 { …} facdocs-consumer-1 | ./vendor/symfony/runtime/Runner/Symfony/ConsoleApplicationRunner.php:54 { …} facdocs-consumer-1 | ./vendor/autoload_runtime.php:29 { …} facdocs-consumer-1 | ./bin/console:11 { facdocs-consumer-1 | › facdocs-consumer-1 | › require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; facdocs-consumer-1 | › facdocs-consumer-1 | arguments: { facdocs-consumer-1 | "/srv/app/vendor/autoload_runtime.php" facdocs-consumer-1 | } facdocs-consumer-1 | } facdocs-consumer-1 | } facdocs-consumer-1 | } facdocs-consumer-1 exited with code 255
  6. Thanks maxxd! Not quite production or even close, unfortunately... Current objective is to provide a workableness system to get frontend peps to better understand what they need to do on their end. I probably shouldn't be going down this dockers path as it seems like a blackhole to me, but the main reasons I am doing so is it allows me to better show the frontend people what they need to interface to by "automatically" creating a super-basic client app. I somewhat got things working, but then restarted the docker, and had SQL problems... I know it can't be magic, but not being able to identify what is initiating the script which is causing me grief makes me kind of hate dockers. I've made some progress and see that it is calling a PHP script to initiate things, but even after I comment out what I think are the culprits, still issues... Will try again tomorrow!
  7. I ended up leaving the folder which are mounted to allow external editing alone and just updated the other files. At first, all seemed good, but then something went wrong. I start the docker using docker compose up, and then I instantly start getting errors like the following. So, I expect that maybe autoload paths need to be regenerated or I have a issue with my composer.json file. Problem is I can update composer as the docker keeps on restarting, and am having a difficult time troubleshooting. I suspect that the docker-compose (and not the DockerFile, right?) file is initiating running some pre-check script and hoping I can temporarily bypass it, but it what initiates it escapes me. Any thoughts? Thank you Cannot autowire service "App\Command\AddSystemUser": argument "$phoneNumberUtil" of method "App\Command\AbstractAddUser::__construct()" references class "libphonenumber\PhoneNumberUtil" but no such service exists. PS. In addition to docker-compose.yml provided above, I also have docker-compose.override.yml. version: "3.4" # Development environment override services: php: build: target: app_php_dev volumes: - ./api:/srv/app - ./api/docker/php/conf.d/app.dev.ini:/usr/local/etc/php/conf.d/app.dev.ini:ro # If you develop on Mac or Windows you can remove the vendor/ directory # from the bind-mount for better performance by enabling the next line: #- /srv/app/vendor environment: # See https://xdebug.org/docs/all_settings#mode XDEBUG_MODE: "${XDEBUG_MODE:-off}" extra_hosts: # Ensure that host.docker.internal is correctly defined on Linux - host.docker.internal:host-gateway consumer: build: target: app_php_dev volumes: - ./api:/srv/app - ./api/docker/php/conf.d/app.dev.ini:/usr/local/etc/php/conf.d/app.dev.ini:ro # If you develop on Mac or Windows you can remove the vendor/ directory # from the bind-mount for better performance by enabling the next line: #- /srv/app/vendor environment: # See https://xdebug.org/docs/all_settings#mode XDEBUG_MODE: "${XDEBUG_MODE:-off}" extra_hosts: # Ensure that host.docker.internal is correctly defined on Linux - host.docker.internal:host-gateway pwa: build: context: ./pwa target: dev volumes: - ./pwa:/srv/app environment: API_PLATFORM_CREATE_CLIENT_ENTRYPOINT: http://caddy API_PLATFORM_CREATE_CLIENT_OUTPUT: . caddy: volumes: - ./api/public:/srv/app/public:ro - ./api/docker/caddy/Caddyfile:/etc/caddy/Caddyfile:ro environment: MERCURE_EXTRA_DIRECTIVES: demo ###> doctrine/doctrine-bundle ### database: ports: - target: 5432 published: 5432 protocol: tcp ###< doctrine/doctrine-bundle ### ###> symfony/mercure-bundle ### ###< symfony/mercure-bundle ###
  8. Vendor classes. Maybe regenerated. How does one regenerate within a docker? docker-compose.yml version: "3.4" services: php: build: context: ./api target: app_php depends_on: - database restart: unless-stopped volumes: - php_socket:/var/run/php healthcheck: interval: 10s timeout: 3s retries: 3 start_period: 30s environment: DATABASE_URL: postgresql://${POSTGRES_USER:-app}:${POSTGRES_PASSWORD:-!ChangeMe!}@database:5432/${POSTGRES_DB:-app}?serverVersion=${POSTGRES_VERSION:-14} TRUSTED_PROXIES: ${TRUSTED_PROXIES:-127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16} TRUSTED_HOSTS: ^${SERVER_NAME:-example\.com|localhost}|caddy$$ MERCURE_URL: ${CADDY_MERCURE_URL:-http://caddy/.well-known/mercure} MERCURE_PUBLIC_URL: https://${SERVER_NAME:-localhost}/.well-known/mercure MERCURE_JWT_SECRET: ${CADDY_MERCURE_JWT_SECRET:-!ChangeThisMercureHubJWTSecretKey!} consumer: build: context: ./api target: app_php entrypoint: docker-php-entrypoint command: bin/console messenger:consume depends_on: - database restart: unless-stopped healthcheck: test: ['CMD', 'ps', 'aux', '|', 'egrep', '"\d+:\d+ php bin/console messenger:consume"'] interval: 10s timeout: 3s retries: 3 start_period: 30s environment: DATABASE_URL: postgresql://${POSTGRES_USER:-app}:${POSTGRES_PASSWORD:-!ChangeMe!}@database:5432/${POSTGRES_DB:-app}?serverVersion=${POSTGRES_VERSION:-14} TRUSTED_PROXIES: ${TRUSTED_PROXIES:-127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16} TRUSTED_HOSTS: ^${SERVER_NAME:-example\.com|localhost}|caddy$$ MERCURE_URL: ${CADDY_MERCURE_URL:-http://caddy/.well-known/mercure} MERCURE_PUBLIC_URL: https://${SERVER_NAME:-localhost}/.well-known/mercure MERCURE_JWT_SECRET: ${CADDY_MERCURE_JWT_SECRET:-!ChangeThisMercureHubJWTSecretKey!} pwa: build: context: ./pwa target: prod environment: NEXT_PUBLIC_ENTRYPOINT: http://caddy caddy: build: context: api/ target: app_caddy depends_on: - php - pwa environment: PWA_UPSTREAM: pwa:3000 SERVER_NAME: ${SERVER_NAME:-localhost}, caddy:80 MERCURE_PUBLISHER_JWT_KEY: ${CADDY_MERCURE_JWT_SECRET:-!ChangeThisMercureHubJWTSecretKey!} MERCURE_SUBSCRIBER_JWT_KEY: ${CADDY_MERCURE_JWT_SECRET:-!ChangeThisMercureHubJWTSecretKey!} MERCURE_EXTRA_DIRECTIVES: demo restart: unless-stopped volumes: - php_socket:/var/run/php - caddy_data:/data - caddy_config:/config ports: # HTTP - target: 80 published: ${HTTP_PORT:-80} protocol: tcp # HTTPS - target: 443 published: ${HTTPS_PORT:-443} protocol: tcp # HTTP/3 - target: 443 published: ${HTTP3_PORT:-443} protocol: udp ###> doctrine/doctrine-bundle ### database: image: postgres:${POSTGRES_VERSION:-14}-alpine environment: - POSTGRES_DB=${POSTGRES_DB:-app} # You should definitely change the password in production - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-!ChangeMe!} - POSTGRES_USER=${POSTGRES_USER:-app} volumes: - db_data:/var/lib/postgresql/data # you may use a bind-mounted host directory instead, so that it is harder to accidentally remove the volume and lose all your data! # - ./api/docker/db/data:/var/lib/postgresql/data ###< doctrine/doctrine-bundle ### # Mercure is installed as a Caddy module, prevent the Flex recipe from installing another service ###> symfony/mercure-bundle ### ###< symfony/mercure-bundle ### volumes: php_socket: caddy_data: caddy_config: ###> doctrine/doctrine-bundle ### db_data: ###< doctrine/doctrine-bundle ### ###> symfony/mercure-bundle ### ###< symfony/mercure-bundle ### api/Dockerfile #syntax=docker/dockerfile:1.4 # Adapted from https://github.com/dunglas/symfony-docker # Prod image FROM php:8.1-fpm-alpine AS app_php ENV APP_ENV=prod WORKDIR /srv/app # persistent / runtime deps RUN apk add --no-cache \ acl \ fcgi \ file \ gettext \ git \ gnu-libiconv \ ; # install gnu-libiconv and set LD_PRELOAD env to make iconv work fully on Alpine image. # see https://github.com/docker-library/php/issues/240#issuecomment-763112749 ENV LD_PRELOAD /usr/lib/preloadable_libiconv.so RUN set -eux; \ apk update; \ apk upgrade -U -a --purge; \ apk add --no-cache --virtual .build-deps \ $PHPIZE_DEPS \ icu-data-full \ icu-dev \ libzip-dev \ zlib-dev \ ; \ \ docker-php-ext-configure zip; \ docker-php-ext-install -j$(nproc) \ intl \ zip \ ; \ pecl install \ apcu \ ; \ pecl clear-cache; \ docker-php-ext-enable \ apcu \ opcache \ ; \ \ runDeps="$( \ scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions \ | tr ',' '\n' \ | sort -u \ | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ )"; \ apk add --no-cache --virtual .app-phpexts-rundeps $runDeps; \ \ apk del .build-deps ###> recipes ### ###> doctrine/doctrine-bundle ### RUN apk add --no-cache --virtual .pgsql-deps postgresql-dev; \ docker-php-ext-install -j$(nproc) pdo_pgsql; \ apk add --no-cache --virtual .pgsql-rundeps so:libpq.so.5; \ apk del .pgsql-deps ###< doctrine/doctrine-bundle ### ###< recipes ### RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" COPY --link docker/php/conf.d/app.ini $PHP_INI_DIR/conf.d/ COPY --link docker/php/conf.d/app.prod.ini $PHP_INI_DIR/conf.d/ COPY --link docker/php/php-fpm.d/zz-docker.conf /usr/local/etc/php-fpm.d/zz-docker.conf RUN mkdir -p /var/run/php COPY --link docker/php/docker-healthcheck.sh /usr/local/bin/docker-healthcheck RUN chmod +x /usr/local/bin/docker-healthcheck HEALTHCHECK --interval=10s --timeout=3s --retries=3 CMD ["docker-healthcheck"] COPY --link docker/php/docker-entrypoint.sh /usr/local/bin/docker-entrypoint RUN chmod +x /usr/local/bin/docker-entrypoint ENTRYPOINT ["docker-entrypoint"] CMD ["php-fpm"] # https://getcomposer.org/doc/03-cli.md#composer-allow-superuser ENV COMPOSER_ALLOW_SUPERUSER=1 ENV PATH="${PATH}:/root/.composer/vendor/bin" COPY --from=composer:2 --link /usr/bin/composer /usr/bin/composer # prevent the reinstallation of vendors at every changes in the source code COPY composer.* symfony.* ./ RUN set -eux; \ composer install --prefer-dist --no-dev --no-autoloader --no-scripts --no-progress; \ composer clear-cache # copy sources COPY --link . . RUN rm -Rf docker/ RUN set -eux; \ mkdir -p var/cache var/log; \ composer dump-autoload --classmap-authoritative --no-dev; \ composer dump-env prod; \ composer run-script --no-dev post-install-cmd; \ chmod +x bin/console; sync VOLUME /srv/app/config/jwt/ # Dev image FROM app_php AS app_php_dev ENV APP_ENV=dev XDEBUG_MODE=off VOLUME /srv/app/var/ RUN rm $PHP_INI_DIR/conf.d/app.prod.ini; \ mv "$PHP_INI_DIR/php.ini" "$PHP_INI_DIR/php.ini-production"; \ mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini" COPY --link docker/php/conf.d/app.dev.ini $PHP_INI_DIR/conf.d/ RUN set -eux; \ apk add --no-cache --update --virtual .build-deps linux-headers $PHPIZE_DEPS; \ pecl install xdebug; \ docker-php-ext-enable xdebug; \ apk del .build-deps RUN rm -f .env.local.php # Build Caddy with the Mercure and Vulcain modules FROM caddy:2-builder-alpine AS app_caddy_builder RUN xcaddy build \ --with github.com/dunglas/mercure \ --with github.com/dunglas/mercure/caddy \ --with github.com/dunglas/vulcain \ --with github.com/dunglas/vulcain/caddy # Caddy image FROM caddy:2-alpine AS app_caddy WORKDIR /srv/app COPY --from=app_caddy_builder --link /usr/bin/caddy /usr/bin/caddy COPY --from=app_php --link /srv/app/public public/ COPY --link docker/caddy/Caddyfile /etc/caddy/Caddyfile
  9. I have the following PHP project, and I DO care about keeping the git history: michael@ubuntu-hp-laptop-original:/var/www/facdocs/api$ ls -la total 1076 drwxr-xr-x 18 michael michael 4096 Feb 11 07:52 . drwxr-xr-x 37 michael michael 4096 Feb 22 18:42 .. drwxrwxr-x 2 michael michael 4096 Sep 16 05:34 bin -rw-rw-r-- 1 michael michael 3814 Feb 15 05:38 composer.json -rw-rw-r-- 1 michael michael 477849 Feb 15 05:38 composer.lock -rw-rw-r-- 1 michael michael 470716 Sep 16 05:25 composer.lock2.tmp drwxr-xr-x 5 michael michael 4096 Feb 22 09:48 config lrwxrwxrwx 1 michael michael 7 Oct 20 04:53 core -> ../core -rw-rw-r-- 1 michael michael 2026 Feb 22 09:40 .env -rw-rw-r-- 1 michael michael 320 Nov 22 06:23 .env.test drwxrwxr-x 2 michael michael 4096 Dec 11 10:29 fixtures drwxrwxr-x 8 michael michael 4096 Feb 22 16:17 .git -rw-rw-r-- 1 michael michael 704 Dec 11 10:29 .gitignore drwxr-xr-x 2 michael michael 4096 Oct 30 15:23 index drwxrwxr-x 2 michael michael 4096 Feb 3 07:01 migrations lrwxrwxrwx 1 michael michael 8 Aug 6 2022 notes -> ../notes -rw-rw-r-- 1 michael michael 197 Sep 16 05:33 .php-cs-fixer.dist.php -rw-rw-r-- 1 michael michael 30546 Feb 3 16:59 .phpunit.result.cache -rw-rw-r-- 1 michael michael 1431 Dec 13 04:14 phpunit.xml.dist drwxrwxr-x 4 michael michael 4096 Feb 22 09:29 public -rw-rw-r-- 1 michael michael 2689 Jul 17 2022 rector.php drwxr-xr-x 3 michael michael 4096 Jul 12 2022 resources drwxr-xr-x 2 michael michael 4096 Oct 10 05:33 source-data drwxrwxr-x 30 michael michael 4096 Nov 9 07:53 src -rw-rw-r-- 1 michael michael 10924 Dec 11 10:29 symfony.lock drwxrwxr-x 2 michael michael 4096 Jun 18 2022 templates drwxrwxr-x 5 michael michael 4096 Feb 15 05:35 testing drwxrwxr-x 2 michael michael 4096 Feb 2 05:50 tests -rw-r--r-- 1 michael michael 1415 Jan 21 11:00 TODO.md drwxrwxr-x 2 michael michael 4096 Sep 16 05:33 translations drwxrwxrwx 6 facdocs michael 4096 Jun 19 2022 var drwxrwxr-x 49 michael michael 4096 Feb 15 05:38 vendor michael@ubuntu-hp-laptop-original:/var/www/facdocs/api$ I have another project which uses dockers and PHP as well as other stuff, and I don't care about keeping the git history: michael@apiplatform-HP-EliteBook-830-G6:~/demo$ ls -la total 64 drwxrwxr-x 7 michael michael 4096 Feb 17 08:35 . drwxr-x--- 26 michael michael 4096 Feb 22 19:08 .. drwxrwxr-x 13 michael michael 4096 Feb 17 09:48 api -rw-rw-r-- 1 michael michael 1859 Feb 17 08:35 docker-compose.override.yml -rw-rw-r-- 1 michael michael 396 Feb 17 08:35 docker-compose.prod.yml -rw-rw-r-- 1 michael michael 3796 Feb 17 08:35 docker-compose.yml -rw-rw-r-- 1 michael michael 1203 Feb 17 08:35 .editorconfig drwxrwxr-x 8 michael michael 4096 Feb 22 19:08 .git -rw-rw-r-- 1 michael michael 319 Feb 17 08:35 .gitattributes drwxrwxr-x 3 michael michael 4096 Feb 17 08:35 .github -rw-rw-r-- 1 michael michael 72 Feb 17 08:35 .gitignore drwxrwxr-x 3 michael michael 4096 Feb 17 08:35 helm -rw-rw-r-- 1 michael michael 1066 Feb 17 08:35 LICENSE drwxrwxr-x 12 michael michael 4096 Feb 22 19:06 pwa -rwxrwxr-x 1 michael michael 2739 Feb 17 08:35 README.md -rwxrwxr-x 1 michael michael 433 Feb 17 08:35 update-deps.sh michael@apiplatform-HP-EliteBook-830-G6:~/demo$ cd api michael@apiplatform-HP-EliteBook-830-G6:~/demo/api$ ls -la total 520 drwxrwxr-x 13 michael michael 4096 Feb 17 09:48 . drwxrwxr-x 7 michael michael 4096 Feb 17 08:35 .. drwxrwxr-x 2 michael michael 4096 Feb 17 08:35 bin -rw-rw-r-- 1 michael michael 3330 Feb 22 19:06 composer.json -rw-rw-r-- 1 michael michael 419938 Feb 22 19:05 composer.lock drwxrwxr-x 5 michael michael 4096 Feb 17 08:35 config drwxrwxr-x 4 michael michael 4096 Feb 17 08:35 docker -rw-rw-r-- 1 michael michael 3977 Feb 17 08:35 Dockerfile -rw-rw-r-- 1 michael michael 390 Feb 17 08:35 .dockerignore -rw-rw-r-- 1 michael michael 2811 Feb 17 09:48 .env -rw-rw-r-- 1 michael michael 215 Feb 17 09:48 .env.test drwxrwxr-x 2 michael michael 4096 Feb 17 08:35 fixtures -rw-rw-r-- 1 michael michael 517 Feb 17 09:04 .gitignore drwxrwxr-x 2 michael michael 4096 Feb 17 08:35 migrations -rw-rw-r-- 1 michael michael 197 Feb 17 08:35 .php-cs-fixer.dist.php -rw-rw-r-- 1 michael michael 1367 Feb 17 09:48 phpunit.xml.dist -rw-rw-r-- 1 michael michael 444 Feb 17 08:35 psalm.xml.dist drwxrwxr-x 3 michael michael 4096 Feb 17 08:35 public drwxrwxr-x 10 michael michael 4096 Feb 17 09:04 src -rw-rw-r-- 1 michael michael 16462 Feb 17 09:48 symfony.lock drwxrwxr-x 2 michael michael 4096 Feb 17 08:35 templates drwxrwxr-x 5 michael michael 4096 Feb 17 09:48 tests drwxr-xr-x 2 root root 4096 Feb 17 08:35 var drwxr-xr-x 42 root root 4096 Feb 22 19:05 vendor michael@apiplatform-HP-EliteBook-830-G6:~/demo/api$ I wish to replace the content in the api folder of the second project with the content in the first one, and either: Keep the second project's git history and make the api folder a git submodule. Get rid of the second project's git history mov api/.git to . so I all the second project's files appear as new in the git history and I have only one git repo. I thought this would be more straight forward, but repeatedly get PHP composer errors about classes not being found. I think it has to do with how some of the folders are mounted, but not positive. Note that I didn't just replace the content in the api folder, but actually replaced the folder, and maybe that is breaking things? And along those lines, maybe not replace app/var and app/vendor either? Thank much
  10. Similarly, if one executes the same provider multiple times, they will receive the same results. I have also read so and recognize that external dependencies add additional dimensions, however, see some benefits of doing so and haven't gotten my head around mock objects. I see your point about issues with yielding the same object, and was thinking of cloning as you suggest. Assuming all agree that I should not attempt to update the database to a given state within the provider but right before actually performing each test.
  11. I wish to test an API whether users with various permissions can perform actions on resources with various access policies. I first envisioned using a provider such as the following, however, quickly found it had at least one major flaw and I am certain more. /** * @dataProvider userResourceProvider */ public function testUserResource(User $user, object $resource, int $expectedHttpStatusCode):void { $client = $this->createClient($user); $client->makeSomeHttpRequest($resource); $this->assertStatusCode($expectedHttpStatusCode); } public function userResourceProvider(): \Generator { $user = new User(); $this->updateUserToInitialState($user); $this->updateDatabase($user); foreach($this->resourceClasses as $resourceClass) { $resource = new $resourceClass(); $this->updateResourceToInitialState($resource); $this->updateDatabase($resource); foreach($this->getResourcePolicy() as $policy) { $resource->setPolicy($policy); $this->updateDatabase($resource); foreach($this->getUserPermission() as $permission) { $user->setPermission($permission); $this->updateDatabase($user); yield [$user, $resource, $this->getExpectedHttpStatusCode($user, $resource)]; } } } } Instead of passing the $user and the $resource in their current states to the test method, it will their last state after the second and third foreach loop which I confirmed using the following. public function someProvider(): \Generator { $obj = new SomeObject(); foreach([4,3,6,1] as $i) { $obj->setIndex($i); yield [$i, $obj]; } } Not only will the state of the objects passed to the test be incorrect, I am almost sure the state of the database storing those objects be incorrect as (I think) all database updates be perform before executing the test methods. Questions: Could/should any portions of modifying the state of the objects and the state of the database be updated in the provider (for instance, updateUserToInitialState and updateResourceToInitialState), or should all be repeated each time within the test method? What are the implications of database transactions when performing tests? Any other common gochas when performing tests on objects of different state?
  12. Actually, I think there is an Option 4, and while it goes down the inheritance path, it doesn't bring all the baggage. Instead of extending some big complicated class, only extend small simple classes which are less likely to require future changes. I also recognize that this solution does as you recommend and uses an intermediary table. Still isn't an alternative to inheritance, but at least makes inheritance less painful. I went back and forth whether Project or ProjectPermissionPolicy should hold the other's PK as neither should exist on its own without the presence of the other. I was going to come up with some awesome abstract Person/Heart analogies, but fortunately decided that when the first rule of thumb doesn't apply, the next rule of thumb should be that the parent table is the one which most logically would be delete. Project - Id (PK) - Data Asset - Id (PK) - Data AbstractPermissionPolicy - Id (PK) - discriminator - Data ProjectPermissionPolicy extends AbstractPermissionPolicy - Id (PK, FK to AbstractPermissionPolicy.id) - resourceId (FK to project.id, delete cascade) AssetPermissionPolicy extends AbstractPermissionPolicy - Id (PK, FK to AbstractPermissionPolicy.id) - resourceId (FK to asset.id, delete cascade) Member - PermissionPolicyId (PK, FK to AbstractPermissionPolicy.Id) - UserId (PK, FK to User.Id) - MemberPermissionData User - Id (PK) - GeneralUserPermissionData - OtherData
  13. Thanks Maxxd, I didn't think of it until you brought it up, but vaguely recall reading some blog awhile ago where they promoted always using an intermediary table. Don't know if I agree with "always", but I do think it helps for some situations. Will give it some thought and definitely consider. Thanks!
  14. Projects and assets act as "document container" and the PermissionPolicy which can be referenced by only 1 project or asset applies to the documents, and right or wrong it isn't desirable to have changes to one container affect another. No concerns about any added work if I ever need to change, but some concerns that it is going against everything I've ever read regarding proper database design. I did a quick google search to make sure I was not just imaging it, and all feverishly state that the FK of the parent table must be stored in child table, and that the parent table is the one which can exists on its own without the presence of the child table. But what do they know, so I pulled out my Simple SQL book to see what Rudy says, and he says that the foreign key goes in the table on the many side of the relationship, and for one-to-ones, it should be the same if it did have a many side (potentially, I misinterpreted the part about one-and-ones, and even if I didn't, he was not as adamant on that part) . Forgetting about my specific use case and a different way of asking this question. Have you ever had the need to move the foreign key from the child table to the parent table? Did you have any issues?
  15. Thanks Maxxd, Glad you didn't totally bash the first schema as it is currently what I am going with! It is just different than anything I've done in the past and I think (thought?) different than what others do, and usually that means I am doing it wrong. Appreciate your response!
  16. I am pretty sure I can guess your table names before I can guess your UUIDs. ULIDs are another option, and while I think they might be less likely to have conflicts than UUIDs (UUIDs have 1 in 1,000,000,000,000,000,000,000 odds of a duplicate), they provide other benefits.
  17. Really hoping to get my dilemma resolved as I have tried to multiple times and it just stays in limbo. I provided more access control detail per request even though my real question relates to how best to allow a single object to have reciprocating relationships to multiple other objects. I recognize that no one has any obligation to help, yet many of you have done so and most of what I know is thanks to you. If there just isn't a good answer, please also let me know that so I may stop searching. I am starting to think that ORMs or at least Doctrine too closely couple the DB schema to the object model, and if I wish to use one, I need to compromise on the database schema or other aspects. Thanks again
  18. I gave a little thought on why I have concerns about my proposed schema changes (Option 1). The problem is I have more concerns about the alternate approaches I could think of (Option 2 and Option 3). Option 1. Object that has/owns a property references that property instead of the other way around. Orphan PermissionPolicy records can exist if no Projects, Assets, etc reference it. Can't delete a Projects, Assets, etc, and cascade delete the PermissionPolicy record. Adds complexity to ensure that a given PermissionPolicy is referenced by only a single Project, Asset, etc. Can't switch from OneToOne to ManyToOne by simply removing the unique constraint. Project - Id (PK) - PermissionPolicyId (FK to PermissionPolicy.Id, UNIQUE) - Data Asset - Id (PK) - PermissionPolicyId (FK to PermissionPolicy.Id, UNIQUE) - Data PermissionPolicy - Id (PK) - RequiredPermissionData Member - PermissionPolicyId (PK, FK to PermissionPolicy.Id) - UserId (PK, FK to User.Id) - MemberPermissionData User - Id (PK) - GeneralUserPermissionData - OtherData Option 2. Unique tables for each entity. I don't care for the duplication. User will have individual collections for each type. Project - Id (PK) - Data Asset - Id (PK) - Data ProjectPermissionPolicy - Id (PK) - ProjectId (FK to Project.Id, CASCADE DELETE, UNIQUE) - RequiredPermissionData AssetPermissionPolicy - Id (PK) - AssetId (FK to Asset.Id, CASCADE DELETE, UNIQUE) - RequiredPermissionData ProjectMember - ProjectPermissionPolicyId (PK, FK to ProjectPermissionPolicy.Id, CASCADE DELETE) - UserId (PK, FK to User.Id) - MemberPermissionData AssetMember - AssetPermissionPolicyId (PK, FK to AssetPermissionPolicy.Id, CASCADE DELETE) - UserId (PK, FK to User.Id) - MemberPermissionData User - Id (PK) - GeneralUserPermissionData - OtherData Option 3. Extend every entity from AbstractBaseEntity. I have a few entities which do use inheritance (i.e. CommonToAllSpecification and CustomUserDefinedSpecification) and I will need to have CommonToAllSpecification joined even though there is no access policy for it. Later adding access control to an entity will require changing the primary keys. AbstractBaseEntity - Id (PK) Project extends AbstractBaseEntity - Id (PK) - Data Asset extends AbstractBaseEntity - Id (PK) - Data PermissionPolicy - Id (PK) - AbstractBaseEntityId (FK to AbstractBaseEntity.Id, CASCADE DELETE, UNIQUE) - RequiredPermissionData Member - PermissionPolicyId (PK, FK to PermissionPolicy.Id, CASCADE DELETE) - UserId (PK, FK to User.Id) - MemberPermissionData User - Id (PK) - GeneralUserPermissionData - OtherData
  19. I am not trying to be vague and the roller coaster example is very close. And while I appreciate your help regarding my approach to access policy, my desire is to determine how best to define the database schema and relate the entity objects. I have provided specific details below and hope that it doesn't distract from what I thought was a simple question regarding modelling using composition instead of inheritance. I have these entities (Project, Asset, Vendor) which have properties and some (Project, Asset, but not Vendor) also act as a container for documents. There are three types of users: AdminUser, InternalUser, and ExternalUser, and internal and external users will typically have different access policies. Admin users have the capability to create the parent entities and specify on a per-record basis the access policy for the parent entity and for the documents which they contain. The access policy for the parent entity determines ability to read and update with possible values ALL/NONE. The access policy for the documents is common to all documents contained in a given parent entity, determines ability to create, read, update, and delete with possible values ALL/OWNER/NONE where OWNER represents the logged on user being the user who created the document (note that create doesn't support OWNER). Both internal and external users may also join as a member to a given parent entity, and their permission policy maybe be modified for the given entity. An example Project is as follows, Asset will look the same, and Vendor will look the same except not have the document permission policies. { "id": "01GFJTS5W4DQGVRD6FT1P5SQNW", "name": "MyProject", "type": "project", "otherProperties": "bla", "internalUserAccessPolicy": { "read_parent": "ALL", "update_parent": "NONE", "create_documents": "ALL", "read_documents": "ALL", "update_documents": "OWNER", "delete_documents": "NONE" }, "externallUserAccessPolicy": { "read_parent": "NONE", "update_parent": "NONE", "create_documents": "NONE", "read_documents": "OWNER", "update_documents": "OWNER", "delete_documents": "NONE" }, "members": [{ "userId": "01GFJTSKMT2VVZF90X89D33S38", "UserPermissionPolicy": { "read_parent": "ALL", "update_parent": "NONE", "create_documents": "ALL", "read_documents": "ALL", "update_documents": "OWNER", "delete_documents": "OWNER" } }, { "userId": "01GFJV47D64FNBRM4YW1HGK49J", "UserPermissionPolicy": { "read_parent": "ALL", "update_parent": "NONE", "create_documents": "ALL", "read_documents": "OWNER", "update_documents": "OWNER", "delete_documents": "NONE" } } ], "documents": [{ "documentId": "01GFJVKAC2CT077865KX97TQVH", "name": "siteplan.pdf", "path": "https://example.com/documents/01GFJV9WV055RZDDYFA4W6MPJC", "owner": "01GFJTSKMT2VVZF90X89D33S38" }, { "documentId": "01GFJV9WV055RZDDYFA4W6MPJC", "name": "drawings.pdf", "path": "https://example.com/documents/01GFJVKAC2CT077865KX97TQVH", "owner": "01GFJTSKMT2VVZF90X89D33S38" } ] } The schema is currently as follows: AbstractResource - id (PK) - name - type - internalUserAccessPolicy (value object) - externallUserAccessPolicy (value object) - otherProperties Project extends AbstractResource - id (PK, one-to-one FK to AbstractResource.id) - otherProperties Asset extends AbstractResource - id (PK, one-to-one FK to AbstractResource.id) - otherProperties User - id (PK) - name Member - resourceId (PK, many-to-many FK to AbstractResource.id) - userId (PK, many-to-many FK to User.id) - userAccessPermission (value object) I am thinking of changing the schema to the following where Project and Asset will now have an AccessPolicy property: ResourceAccessPolicy - id (PK) - discriminator (ResourceAccessPolicy or DocumentAccessPolicy) - internalUserResourceAccessPolicy (value object) - externallUserResourcePolicy (value object) DocumentAccessPolicy extends ResourceAccessPolicy - id (PK, one-to-one FK to ResourceAccessPolicy.id) - internalUserDocumentAccessPolicy (value object) - externallUserDocumentPolicy (value object) Project - id (PK, one-to-one FK to DocumentAccessPolicy.id) - discriminatorCopy (FK to ResourceAccessPolicy.className in order to prevent multiple resources from being associated with the same ResourceAccessPolicy) - otherProperties Asset - id (PK, one-to-one FK to DocumentAccessPolicy.id) - discriminatorCopy (FK to ResourceAccessPolicy.className in order to prevent multiple resources from being associated with the same ResourceAccessPolicy) - otherProperties Vendor - id (PK, one-to-one FK to ResourceAccessPolicy.id) - discriminatorCopy (FK to ResourceAccessPolicy.className in order to prevent multiple resources from being associated with the same ResourceAccessPolicy) - otherProperties User - id (PK) - name Member - accessPolicyId (PK, FK to ResourceAccessPolicy.id) - userId (PK, FK to User.id) - userAccessPermission (value object) Maybe it is obvious that I should do it this way, but I've never done it this way and for unknown reasons, have concerns. While I will gladly accept any access policy specific critique, please also let me know whether there is anything fundamentally wrong with this schema approach and whether I should make any changes.
  20. I like the roller coaster analogy! Yep, the roller coaster access policy is as follows: Giant Dipper Ride with an adult: 36" tall or 6 years old Ride solo: 48" tall or 13 years old American Eagle Ride with an adult: 32" tall or 9 years old Ride solo: 38" tall or 15 years old The Fury Ride with an adult: 38" tall or 5 years old Ride solo: 42" tall or 16 years old And, not just roller coasters, but similar requirements are used for ferris wheels, haunted houses, bumper cars, and carousels which are modelled slightly differently than roller coasters. If the user doesn't meet these requirements, they may take a specialized class specifically for a given ride, and based on the grade they received, the requirements are reduced. I model it as such, but later, there is some new requirement and I find the inheritance prevents the flexibility to make the required changes. So, how should I model it? AbstractRide - id (PK) - minimum_height_with_adult - minimum_age_with_adult - minimum_height_solo - minimum_age_solo - discriminator (roller_coaster, ferris_wheel, etc) - applicableCommonData RollerCoaster extends AbstractRide - id (PK, FK to AbstractRide.id) - applicableData FerrisWheel extends AbstractRide - id (PK, FK to AbstractRide.id) - applicableData HauntedHouse extends AbstractRide... UserTakesRideClass - ride_id (PK, FK to AbstractRide.id) - user_id (PK, FK to User.id) - grade User - id - height - age
  21. Thank you requinix and maxxd, Sorry about the lack of details and misused definitions. "Resource" is just an entity class and "access control" is the required permission needed to access a particular record. In addition, there is a ResourceMember object which is associated with a single User and a single resource object which also has required permission data, and this is the part which I am struggling with. One approach I might take is something like: AbstractResourceUnderAccessControl - id (PK) - discriminator - applicableData ResourceUnderAccessControlOne extends AbstractResourceUnderAccessControl - id (PK, FK to resource_under_access_control.id) - applicableData ResourceUnderAccessControlTwo extends AbstractResourceUnderAccessControl - id (PK, FK to resource_under_access_control.id) - applicableData ResourceMember - resource_under_access_control_id (PK, FK to resource_under_access_control.id) - user_id (PK, FK to user.id) - applicableData But doing it this way paints me into a corner, and would like to consider having each ResourceUnderAccessControl and the associated AccessControl different objects and using composition. But it just seems wrong having all these independent ResourceUnderAccessControl entities having a one-to-one relationship to a single AccessControl entity. In addition to making it more complicated to have the database enforce an AccessControl object to only be associated with a single resource, there is also the concern of later putting another resource under access control and having conflicting IDs, however, for this situation, I am using ULIDs and am not worried about it. While I very much appreciate your specific recommendations and advise how to best solve my immediate access control need, I also very much want to better figure out this generic concept as I have been struggling with it literally for years (composition-over-inheritance-examples, alternatives-to-entity-inheritance, favor-traits-with-interfaces-over-inheritance). Is there anything fundamentally wrong with doing it as I showed in my initial post?
  22. I need certain entity classes (i.e. ResourceUnderAccessControOne and ResourceUnderAccessControlTwo) to have more granular access control, and will need to store applicable for each resource on a per-record basis. I am using Doctrine, and one way to do so is have ResourceUnderAccessControlOne and ResourceUnderAccessControlTwo extend AccessControl, however, there are certain shortcomings with Doctrine and inheritance. As an alternative approach, I am thinking of using a one-to-one between each individual resource class and Acl, however, for no particular reason, I have concerns with this approach. Should I? The following provides a bit more context. I do not believe AccessControl will ever need to directly get the resource tied to it, but the resource will need to directly access its associated AccessControl object. For this schema, ResourceUnderAccessControlOne and ResourceUnderAccessControlTwo can share the same AccessControl which goes against business rules, however, either I can have PHP enforce this rule or add a "type" column to all tables and add the appropriate foreign keys. While "maybe" I shouldn't be implementing access control this way, I would still hope for advise for the stated question. Thanks! AccessControl - id (pk) - applicableDataForAccessControl ResourceUnderAccessControlOne - id (pk, FK to AccessControl.id) - applicableDataforResourceOne ResourceUnderAccessControlTwo - id (pk, FK to AccessControl.id) - applicableDataforResourceTwo
  23. Yeah, the Permission vs PermissionEnum thing kind of confusing to me as well! Yes, Doctrine has generic support for them. Will downgrade and wait it out. Thanks PS. I had tried to throw an __isString() in my enum, but evidently doing so is a no-no. Guess an __isInt() is a bit too niche and I understand why it doesn't exist.
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.