Jump to content

NotionCommotion

Members
  • Posts

    2,446
  • Joined

  • Last visited

  • Days Won

    10

Posts posted by NotionCommotion

  1. 28 minutes ago, kicken said:

    Are you trying to search the contents of the documents, or just the metadata (name, category, etc)? 

    I've never looked into them much, but there are dedicated search servers you could look at as yet another alternative (Apache Solr being one example I've heard of).

    Definitely, metadata will be searched and "maybe" content will be searched for some document types, but not sure.

    Per your link, "Solr is the popular, blazing-fast, open source enterprise search platform built on Apache Lucene", and it appears that Apache Lucene is "just?" a Java library used for the full text search of documents.  If only full text searching, think it is necessary or just use PostgreSQL's?  Also, not sure yet whether my referenced textai is much more, and really haven't spent much time learning about full text searching.

    Trying go understand how the app database and searching using a dedicated server work together.  I't my understanding that I send stuff I wish to later search for to Solr/Lucerne/TextAi which in turn indexes it so it later be searched for.  Seems like the same metadata would then be both in my SQL DB as well as the search engine DB which seems wierd.

  2. I am working on a SQL backed document management API where often the documents will reside in one of several categories (i.e. subject, type, project which created it, etc).

    I am looking into different ways to implement search functionality such as...

    1. Standard WHERE with LIKE and maybe regex.
    2. Full-Text queries (I happen to be using PostgreSQL)
    3. 3rd party Semantic search libraries such as https://github.com/neuml/txtai
    4. 3rd party machine learning libraries such as https://php-ml.readthedocs.io/en/latest/
    5. API calls to some 3rd party webservice.
    6. Other?

    Is there any approach which is best for most applications?  Or as I expect is the "best" approach based on the actual application requirements, and if so, can you please share your decision criteria?

  3. The following works, but seems like it has one more loop than it should.  Better way to do so?  I could get rid of the generator and merge to an array, but the generator seems like a better solution.   Thanks

        private function doSomething(File $file)
        {
            foreach($this->getTextFiles($file) as $textFile) {
                // ...
            }
        }
    
        private function getTextFiles(File $file):\Generator
        {
            foreach($file->getChildren() as $child) {
                if($child instanceof IsTextFile) {
                    yield $child;
                }
                else {
                     foreach($this->getTextFiles($child) as $grandChild) {
                         yield $grandChild;
                     }
                }
            }
        }

     

  4. This is probably a stupid question.

    I wish to get a collection of classes which exist in a given directory, and found this post which directed me to this class, but it is depreciated and appears that this class should now be used.

    Great, I have Composer, and all I need to do is call Composer\Autoload\ClassMapGenerator::createMap($pathToDirectory)!

    Or do I?  The only files included in vendor/composer are below.

    • autoload_classmap.php
    • autoload_files.php
    • autoload_namespaces.php
    • autoload_psr4.php
    • autoload_real.php
    • autoload_static.php
    • ClassLoader.php
    • installed.json
    • installed.php
    • InstalledVersions.php
    • LICENSE
    • platform_check.php

    I expect this is due to how I installed composer globally.

    Should composer classes ever be used in an application other than for dependency management?  Or should I just create my own class for this need?

    image.png

  5. 43 minutes ago, kicken said:

    If you need an actual file path, then tempnam() is the way to go.   tmpfile is for if you need a temporary file resource, just a shortcut essentially for doing fopen('php://temp', 'w+');.

    As an example, I have a html2pdf class that generates some temp files and calls an external program to generate the PDF.  This is how that is coded (roughly).

    public function generatePdf(){
    	$pdf = null;
    
    	try {
    		$source = tempnam(sys_get_temp_dir(), 'htmlpdf');
    		$destination = tempnam(sys_get_temp_dir(), 'htmlpdf');
    
    		file_put_contents($source, $this->html);
    
    		$cmd = $this->createCommandLine($source, $destination);
    		exec($cmd, $output, $ret);
    		if ($ret !== 0){
    			throw new \RuntimeException('Failed to generate PDF with command [' . $cmd . '] Output: ' . implode(PHP_EOL, $output));
    		}
    
    		$pdf = file_get_contents($destination);
    	} finally {
    		unlink($source);
    		unlink($destination);
    	}
    
    	return $pdf;
    }

    Generate the two temporary file locations, execute the command, then read the content out of the file.  The temporary files are then cleaned up using a finally clause which will run whether the function when the function exits, whether that be by a successful return or by throwing an exception.

     

    Thanks kicken,  Was just trying to get out of the measly two unlink statements, and will stop trying.

  6. 1 hour ago, requinix said:

    Why do it that way since all you're doing is reinventing tempnam?

    Yes, but you missed the important part: "when there are no remaining references to the file handle". Which is exactly the case when you use the handle, call stream_get_meta_data on it, and then forget the handle existed.

    I didn't think I was quite reinventing tempnam().  tempnam() doesn't delete the file when complete, correct?  The example in the doc shows unlinking($tmpfname).

    However, if calling stream_get_meta_data() deletes the file and I just create it back, and the PHP no longer thinks it is responsible to delete it, suppose I am reinventing.

  7. I will be using exec() to execute an external program which requires a file output path and will create a file located at that path, and the file is only needed during the execution of the PHP script and should be deleted after the script ends.

    Good or bad idea?  If bad, why?  Thanks

        public function tmpFile():string
        {
            return stream_get_meta_data(tmpfile())['uri'];
        }


    EDIT - Does "when the script ends" happen about the same time as the class's destructor?

    Quote

     

    The file is automatically removed when closed (for example, by calling fclose(), or when there are no remaining references to the file handle returned by tmpfile()), or when the script ends.

    Caution

    If the script terminates unexpectedly, the temporary file may not be deleted.

     

     

  8. 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).

  9. 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();
        }
    }

     

  10. 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.

  11. 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


     

  12. 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!

  13. 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 ###
    

     

  14. 8 hours ago, requinix said:

    Whose classes? Are there maybe autoload paths that need to be regenerated?

    Vendor classes.  Maybe regenerated.   How does one regenerate within a docker?

     

    1 hour ago, maxxd said:

    What's your docker-compose file look like? We need to see that in order to say if it's an issue with the volume mounts.

    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


     

  15. 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:

    1. Keep the second project's git history and make the api folder a git submodule.
    2. 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

  16. 20 hours ago, kicken said:

    To me, it sounds like that means your data provider will be fully iterated before the unit test is run, which would match the behavior you seem to be having.

    It's my understanding of unit tests that you generally should avoid having some state / external dependency.  You should instead create mock objects as necessary to return the data you want. 

    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.

    15 hours ago, requinix said:

    Yielding the same object multiple times in a generator creates the sorts of problems...

    I recommend cloning the $obj each time - it's a test so who cares about performance and memory usage?

    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.

  17. 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:

    1. 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?
    2. What are the implications of database transactions when performing tests?
    3. Any other common gochas when performing tests on objects of different state?
  18. 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

     

  19. On 11/6/2022 at 7:31 PM, maxxd said:

    As to the primary key/foreign key part - just for me - unless I know it's going to be a one-to-one relationship forever I'll assume it's going to turn into a many-to-many at some point. It's probably just my innate desire/tendency to over-engineer so, so many things, but I have gotten to a point where it's no harder to reason about than a simple one-to-one link. It's just me and YMMV, but I personally have never found it harder to code on a one-to-one with an intermediary table than it is to refactor a one-to-one relationship into any other relationship type.

    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!

  20. 15 hours ago, maxxd said:

    Why do you need a PermissionPolicy to be referenced by only 1 project or asset? Seems to me that permissions would span projects and assets at least, unless there's a very specific set of business logic that dictates this. I have to admit I can't imagine what that would be, but I'm not a business person, so...

    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.

    16 hours ago, maxxd said:

    Is this a concern? If there's a possibility of needing a "many-to-one" relationship, IMO (obviously) there's no need to think about a one-to-one and it's probably better to consider the possibility of a many-to-many relationship just in terms of future-proofing. It doesn't add that much cognitive load to have a linking table even if it may not be strictly necessary at the time, but it gives teams the possibility of expanding scope without too much ... well, cussing to be honest.

    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?

×
×
  • 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.