Jump to content

maxxd

Gurus
  • Posts

    1,657
  • Joined

  • Last visited

  • Days Won

    51

Posts posted by maxxd

  1. Pardon the unsolicited advice, but unless your DB class does something really special it looks like you're making things far more difficult than they need to be. PDO is already an abstraction class; just use it. Your query() method and call can be done as such:

    $qry = "
    	SELECT	 username
    	FROM users
    	WHERE username = ?
    ";
    
    $sql = $pdo->prepare($qry);
    $sql->execute(['TechnoDiver']);
    
    die("<pre>".var_export($sql->fetchAll(), true)."</pre>");

    And obviously your query is meant for SELECT statements, but keep in mind if you're planning on extending to INSERT or UPDATE the performance benefits of prepare(). From the documentation:

    Quote

    Calling PDO::prepare() and PDOStatement::execute() for statements that will be issued multiple times with different parameter values optimizes the performance of your application by allowing the driver to negotiate client and/or server side caching of the query plan and meta information.

    So, at that point you're forced to recreate the prepare() method in order to bind multiple arrays of values to the same query.

    • Like 1
  2. I've not used docker remotely, and honestly unless you're deploying to a docker instance I'm not sure I see the benefit in doing so but that's probably just my inexperience as I still typically only use Docker for development. To answer some of your questions I'm going to point you to the docker-compose specifications. For instance, I think you've got the volume definition backward in your mind - it's ../relative/path/on/host:/absolute/path/on/container (the relative path for the host is based on the folder the docker-compose.yml file is in).

    As to your specific questions, I'll do the best I can:

    1. I'm not a server jock by any stretch, but AFAIK a single port can only be used by a single service. So my using 3308 for the host port on this particular MySQL container is because I've got a couple other image ports using 3306 and 3307 for their MySQL containers.
    2. My php files are located in the ../project directory as relative to the docker-compose.yml file. That's why the volume map in the laravel service is ../project:/var/www/html - the apache.conf file I've copied in the Dockerfile has the directive DOCUMENT_ROOT=/var/www/html/public. That way, Laravel files are served from localhost:8001 (note that the ports assignment in the laravel service are 8001:80 - host port 8001 maps to docker port 80).
    3. http{s}://host.docker.internal is most useful when setting up local dev versions of external endpoints within the same Docker network. For instance, if you're using the setup above, the Laravel .env DB_HOST value is 'database' - if you have a .env value for (for instance) AWS_S3_BUCKET_ENDPOINT and you're using the localstack service, that value would be http://host.docker.internal:4566.

    Now all this having been said, I've recently transitioned back to a situation where I'm dealing with multiple sites on single servers instead of several to many different servers that all work together as much as they can (in other words, I moved back to an agency from an app development house) and I'm finding keeping the ports and service names orderly and avoiding clashes to be a bit difficult in Docker. MAMP's ability to assign different local addresses without having to specify a port number in the URL is, quite frankly, a lifesaver. I'm still trying to figure out how it's done and to port that to my Docker setup, but so far it's not panned out.

  3. I see the laravel:new, mysql:5.6, localstack/localstack:latest, and redis:latest images up and running so assuming you've got laravel set up in the ../project directory and the apache.conf file that gets copied into the image has its document_root set to /var/www/html/public, you should be able to go to http://localhost:8001 and see it.

    You'll not see the php-7.4-apache or debian instances in the -ps because they've been renamed. I set the 'image' property of the laravel service to 'laravel:new', so that's the image name you'll see. You should be able to connect to the instance using `docker exec -it laravel /bin/bash`. Ironically though, I'm writing this post on a system without docker so I can't test that. If you use VSCode with Microsoft's Docker extension you can see the name you'll need to use to connect - as I recall it's either 'laravel' or something like 'docker_laravel_1'.

  4. You know those times where explaining your issue gets you to think about it a different way and you immediately solve your problem? Yeah.

    For anyone that happens across this with a similar issue, the problem was that the value being inserted was '', not null. So the duplicate entry was actually valid. I created a mutator in the model to return an actual null value if the attendee_code is empty, and everything works as expected now.

    Thanks again to this forum for being my eternal rubber ducky.

    • Like 1
  5. Hi y'all. In my attendees table the attendee_code field must be unique if it is known - if not, we pretend it's fine and move on. I know that it's not the greatest idea in terms of data integrity, but given the business rules I have to work with, it is what it is. The only problem is that I'm getting a duplicate entry exception when a new record is entered into the DB with a null attendee_code if any other record has a null attendee_code. MySQL allows this, so I'm thinking this must be an eloquent thing - does anyone know what I'm missing?

        public function up()
        {
            Schema::create('attendees', function (Blueprint $table) {
                $table->id();
                $table->timestamps();
                $table->string('first_name');
                $table->string('last_name');
                $table->string('specialty');
                $table->string('attendee_code')->nullable();
                $table->string('npi_id')->nullable();
                $table->string('address_1')->nullable();
                $table->string('city')->nullable();
                $table->string('state')->nullable();
                $table->string('zip_code')->nullable();
                $table->unique('attendee_code');
            });
        }

     

  6. 21 minutes ago, NotionCommotion said:

    why do the images come with more than just the operating system (i.e. PHP 7.4 with Apache for maxxd's example) instead of just the operating system (Debian) and then installing PHP and Apache using apt-get?

    The images you're using from Dockerhub are collections of underlying containers. So if you go to the repo for the official PHP image you can see all the background work that goes into creating it. I use the PHP base image because the docker-php-ext-* functions abstract away a lot of the more fiddly aspects of setting up PHP that I either don't like or don't know. Also, it was built by a lot of people who are far, far better at shell scripting and docker config than I.

  7. Basically, yeah. the image is an official PHP image and it contains some helper methods that make setting things up much easier. To map your list:

    1. Yes, this is PHP 7.4 with Apache on a Debian base
    2. This is updating the apt package manager so it's got the most recent packages, then install packages I know I'm going to need for my environment
    3. Yup - this is copying customized config files
    4. Yup
    5. Install xdebug and redis PHP interfaces
    6. Actually, I don't recall why this is done - I know there was a reason when I created the file, but it's been a long couple weeks and I can't remember now...
    7. Yeah - customized PHP config
    8. This is one of the helper methods that make things easier - installing php extensions
      1. Yeah, in the second step I'm installing Debian zip for the OS, this step installs the php extension
    9. Bingo - enable the extensions I know I'm going to need
    10. Cleaning up - no need to keep installation files
    11. COPY --from will actually copy from the named instance, so this copies the specified files from the composer:latest image into the specified files in this image
    12. ***See below***
    13. This just installs the AWS CLI API
    14. I built this for Laravel and I find typing 'php artisan' annoying at times, so I tried this as a work-around (not gonna lie, it didn't work)

    *** Installing Node/npm ***

    I find dealing with Node installer annoying - nvm lets me switch versions at will, which is very important in the work I do right now. But there's not an official or recently updated nvm base image on dockerhub so I installed it myself (this is one of the non-best-practices part I spoke of earlier). However, you'll need to source your .bashrc after install before you can use nvm, which necessitated the SHELL command. The -lc parameters passed in let bash start in a login shell state, and read the commands from strings. So basically all of that is an equivalent to exec() that copies nvm from github to local, runs it through bash to install, then symlinks the executable directory into the path directories, installs node v12.x, and finally sources .bashrc before running npm install to pull any dependencies in the project directory.

  8. I'm not going to pretend that @requinix's explanation isn't far beyond what I could do given the topic, but the way I see it is that if you can use a discrete server for a service, that's a separate service (image from DockerHub) in docker-compose. I know it's not considered best practice in the world of Docker but I have no problem installing programs and apps on a service container in Docker - for instance, if I need Composer, I'll gladly install it in my Dockerfile for the PHP service. However, if I need MySQL I'll use the official image from DockerHub as a separate service in my docker-compose file. Same with Redis, AWS (LocalStack), and MailHog. For instance:

    docker-compose:

    version: '3.5'
    
    services:
      laravel:
        build:
          context: .
        image: laravel:new
        volumes:
          - ../project:/var/www/html
          - ../logs/:/var/log/
        ports:
          - 8001:80
        environment:
          - ENVIRONMENT=development
        networks:
          - laravel
        depends_on:
          - redis
          - database
    
      database:
        image: mysql:5.6
        ports:
          - 3308:3306
        environment:
          - MYSQL_ROOT_PASSWORD=mypass
          - MYSQL_DATABASE=laravel
          - MYSQL_USER=myuser
          - MYSQL_PASSWORD=mypass
        # command: '--innodb-flush-method=fsync'
        volumes:
        # NOTE: this is not possible in mysql 5.7+ - you'll get an error that the directory
        # is not empty, therefor is unusable. gotta figure that out.
          - ./sqldump/mysql/init/:/var/lib/mysql/
          - ./sqldump/mysql/init/:/docker-entrypoint-initdb.d
        networks:
          - laravel
    
      redis:
        image: redis:latest
        ports:
          - 6379:6379
        volumes:
          - ./sqldump/redis:/data
          - ./redis.conf:/usr/local/etc/redis/redis.conf
        networks:
          - laravel
    
      localstack:
        image: localstack/localstack:latest
        ports:
          - 4566:4566
          - 8080:8080
        environment:
          - DEBUG=1
          - DATA_DIR=../logs/localstack
          - PORT_WEB_UI=8080
          - DOCKER_HOST=unix:///var/run/docker.sock
          - USE_SSL=false
        volumes:
          - ./data:/tmp/localstack
          - /var/run/docker.sock:/var/run/docker.sock
          - ./localstack/aws_services.sh:/docker-entrypoint-initaws.d
        networks:
          - laravel
    
    networks:
      laravel:
        name: laravel

    Dockerfile:

    FROM php:7.4-apache
    
    RUN apt-get update
    
    RUN apt-get install vim -y
    
    RUN apt-get install -y \
    	git \
    	zip \
    	curl \
    	sudo \
    	unzip \
    	libicu-dev \
    	libbz2-dev \
    	libpng-dev \
    	libjpeg-dev \
    	libmcrypt-dev \
    	libreadline-dev \
    	libfreetype6-dev \
    	libonig-dev \
    	libzip-dev
    
    COPY apache.conf /etc/apache2/sites-available/000-default.conf
    
    COPY redis.conf /usr/local/etc/redis/redis.conf
    
    RUN a2enmod rewrite headers
    
    RUN pecl install xdebug \
    	pecl install redis
    
    RUN rm -rf /usr/local/etc/php/conf.d/docker-php-ext-redis.ini \
    	rm -rf /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
    
    COPY ./php.ini /usr/local/etc/php/php.ini
    
    RUN docker-php-ext-install \
    	bz2 \
    	intl \
    	iconv \
    	bcmath \
    	opcache \
    	calendar \
    	mbstring \
    	pdo_mysql \
    	mysqli \
    	pcntl \
    	zip \
    	gd \
    	gettext
    
    RUN docker-php-ext-enable \
    	bz2 \
    	intl \
    	iconv \
    	bcmath \
    	sodium \
    	opcache \
    	calendar \
    	mbstring \
    	pdo_mysql \
    	mysqli \
    	pcntl \
    	zip \
    	xdebug \
    	gd \
    	redis \
    	gettext
    
    RUN docker-php-ext-configure gd
    
    RUN docker-php-source delete
    
    # Composer
    COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
    
    # Install npm and node
    SHELL ["bash", "-lc"]
    RUN curl --silent -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
    RUN source ~/.bashrc
    RUN nvm install v12.16.3
    RUN ln -s ~/.nvm/versions/node/v12.16.3/bin/node /usr/bin/node
    RUN ln -s ~/.nvm/versions/node/v12.16.3/bin/npm /usr/bin/npm
    RUN ln -s ~/.nvm /usr/bin/.nvm
    
    # AWS PHP SDK
    RUN composer require aws/aws-sdk-php
    
    # Customization
    RUN echo "alias artisan='php artisan'" > ~/.bashrc
    

    My project directory for this particular one is as such:

    project_setup.png

  9. There are some fundamentals that you need to understand. Take a look at variable scope and how to pass variables from one function/method to another, visibility and how it affects properties and methods, and static methods and properties.

    Once you work on that you can look at the pattern you're using - right now you're going from the outside in. You're calling to the display functions and asking them to include the functions that do the work. You want to call the functions that do the work, store the result of that work, then pass that to the display class.

    • Like 1
  10. It sounds like something's confused in what you're doing. Are you calling the Display class now instead of the Post class? And either way, if your IDE is giving you red squigglies in the Display class then Display clearly does use the variables. It sounds like you want to still call the Post class as the instigator, then call from the Post class to the Display class for output - this way your interface to the overall system doesn't change, and you can only pass the parameters that are needed to the Display class methods. It also allows you to swap out Display classes is you need to at some point, as long as the interface is consistent.

    Post some actual code and we can take a look.

  11. What's your docker-compose file look like?

    Also, I recommend using docker locally for development as that way it's completely portable. You'll be able to work off line, and every system you (or a teammate) uses to develop on will be identical. Getting the docker-compose file correct will build all the local containers, so it's really not an extra step.

  12. Assuming you're just looking at your development environment, you're going to want to use Docker on your local PC. Docker Desktop for Windows makes the installation straight-forward, but creating a custom docker environment can be confusing. As requinix says, using multiple smaller images will make your life easier in long run so look at the documentation for docker-compose. If you start from a basic php image it's fairly simple to set up a local development environment (feel free to DM me if you have questions).

    I will admit that deploying via docker is something I'm still exploring and don't have a lot of experience with, so I can't be much help on that side of things at  the moment.

  13. You can use plain php curl, but honestly I think it'll be easier to deal with if you use Guzzle. I find it much easier, especially when dealing with passing tokens and keys and whatnot; as the docs say:

    $client = new GuzzleHttp\Client();
    $res = $client->request('GET', 'https://api.github.com/user', [
        'auth' => ['user', 'pass']
    ]);
    echo $res->getStatusCode();
    // "200"
    echo $res->getHeader('content-type')[0];
    // 'application/json; charset=utf8'
    echo $res->getBody();
    // {"type":"User"...'

    Give it a shot and come back with code if something goes weird.

  14. If you've got files that are strictly php, don't use the close tag (?>). More than likely, this is where your issue is coming from - some IDEs will add a new line at the end of files. If the php is closed, this is output to the browser and will blow up any header interaction. If the php is not closed, however, it'll be interpreted as whitespace in the code and ignored.

    • Like 1
  15. OK, now we've got the part of the code that matters. It would also be very helpful if you would provide any errors that are happening, or just what is happening in general.

    That being said, there are a few things; first and foremost, your mail host is smtp.office365.com, but you've turned off SMTP in PHPMailer by commenting out the $mail->isSMTP() line. Secondly, as far as I know, there isn't a `smtp/PHPMailerAutoload.php` file - the instructions tell you to include `vendor/autoload.php` and then use the PHPMailer, SMTP, and Exception namespaces. And finally, turn on verbose error reporting with the line $mail->SMTPDebug = SMTP::DEBUG_SERVER and see what's actually happening behind the scenes.

    One more thing I forgot. send() doesn't return a boolean, it throws an error on failure so put all of the PHPMailer code inside a try...catch block.

  16. It's a prepared statement, hence the prepare() method called on the $connection object. It's the correct way to run a query in PHP these days, so well done! In the SQL statement passed to the prepare() method, the `:username`, `:emailbox`, and `:passwordbox` are placeholders in the query. When you pass an array to execute(), that array contains the values to use in those placeholders, so the ':username', ':emailbox', and ':passwordbox' there are keys for the array so that SQL knows which value to plug in to each placeholder.

    Basically, using prepared statements blocks a potential avenue that hackers can use to attack your database or get your data. There are other benefits to prepared statements, but that's kind of the biggest and more pertinent for most systems.

    • Like 1
  17. To tell you the truth, I thought rebooting a server would destroy the cache - I think it used to maybe? Or maybe I'm just remembering incorrectly. At any rate, yes ElastiCache is strictly AWS though I think everyone has their own versions. It's also easy to get mixed up because AWS also has it's own ElasticSearch implementation that isn't called ElastiCache and isn't related in any way. Because apparently they ran out of 2 and 3 letter abbreviations to use...

    Anyhow, I wasn't involved in setting it up but from the AWS console it looked to me like ElastiCache basically is a Redis cluster. And though I disagree with how we used Redis in the past, I will say there were only a handful of times it fell out of sync or lost data, and those were due to bugs in the code and not server or service failure. And if you're using it as intended it really is just a cache, so if you do lose something it shouldn't be a catastrophic loss - in my case, it was several weeks to a month or so of playing catch-up and rehydrating accounts that were affected but we didn't know until the user complained.

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