Categories
Software Development Web Development

My symfony command line cheat sheet

I can never remember all the symfony commands. So I will add them here one at a time as I need them to create a cheat sheet.

Cache – clearing the cache explained

Clear all the caches

php bin/console cache:pool:clear cache.global_clearer

 

Categories
Software Development Web Development

Symfony 5+ using the command line and listing commands

Symfony has useful command line commands. But what are the available commands and how do you get a list?

To get started type the following in the command line in your projects main/root folder. This is the directory that contains the directories src,assets, bin, public, node_modules etc.
php bin/console -help
That command will output the following

bin console help
Output of php bin/console -help command

This shows we can use bin/console list and a few more. Now try the command and see what the list shows.


Available commands:
  about                                      Displays information about the current project
  help                                       Displays help for a command
  list                                       Lists commands
 assets
  assets:install                             Installs bundles web assets under a public directory
 cache
  cache:clear                                Clears the cache
  cache:pool:clear                           Clears cache pools
  cache:pool:delete                          Deletes an item from a cache pool
  cache:pool:list                            List available cache pools
  cache:pool:prune                           Prunes cache pools
  cache:warmup                               Warms up an empty cache
 config
  config:dump-reference                      Dumps the default configuration for an extension
 dbal
  dbal:run-sql                               Executes arbitrary SQL directly from the command line.
 debug
  debug:autowiring                           Lists classes/interfaces you can use for autowiring
  debug:config                               Dumps the current configuration for an extension
  debug:container                            Displays current services for an application
  debug:event-dispatcher                     Displays configured listeners for an application
  debug:form                                 Displays form type information
  debug:router                               Displays current routes for an application
  debug:translation                          Displays translation messages information
  debug:twig                                 Shows a list of twig functions, filters, globals and tests
  debug:validator                            Displays validation constraints for classes
 doctrine
  doctrine:cache:clear-collection-region     Clear a second-level cache collection region
  doctrine:cache:clear-entity-region         Clear a second-level cache entity region
  doctrine:cache:clear-metadata              Clears all metadata cache for an entity manager
  doctrine:cache:clear-query                 Clears all query cache for an entity manager
  doctrine:cache:clear-query-region          Clear a second-level cache query region
  doctrine:cache:clear-result                Clears result cache for an entity manager
  doctrine:database:create                   Creates the configured database
  doctrine:database:drop                     Drops the configured database
  doctrine:database:import                   Import SQL file(s) directly to Database.
  doctrine:ensure-production-settings        Verify that Doctrine is properly configured for a production environment
  doctrine:fixtures:load                     Load data fixtures to your database
  doctrine:mapping:convert                   [orm:convert:mapping] Convert mapping information between supported formats
  doctrine:mapping:import                    Imports mapping information from an existing database
  doctrine:mapping:info                      
  doctrine:migrations:current                [current] Outputs the current version
  doctrine:migrations:diff                   [diff] Generate a migration by comparing your current database to your mapping information.
  doctrine:migrations:dump-schema            [dump-schema] Dump the schema for your database to a migration.
  doctrine:migrations:execute                [execute] Execute one or more migration versions up or down manually.
  doctrine:migrations:generate               [generate] Generate a blank migration class.
  doctrine:migrations:latest                 [latest] Outputs the latest version
  doctrine:migrations:list                   [list-migrations] Display a list of all available migrations and their status.
  doctrine:migrations:migrate                [migrate] Execute a migration to a specified version or the latest available version.
  doctrine:migrations:rollup                 [rollup] Rollup migrations by deleting all tracked versions and insert the one version that exists.
  doctrine:migrations:status                 [status] View the status of a set of migrations.
  doctrine:migrations:sync-metadata-storage  [sync-metadata-storage] Ensures that the metadata storage is at the latest version.
  doctrine:migrations:up-to-date             [up-to-date] Tells you if your schema is up-to-date.
  doctrine:migrations:version                [version] Manually add and delete migration versions from the version table.
  doctrine:query:dql                         Executes arbitrary DQL directly from the command line
  doctrine:query:sql                         Executes arbitrary SQL directly from the command line.
  doctrine:schema:create                     Executes (or dumps) the SQL needed to generate the database schema
  doctrine:schema:drop                       Executes (or dumps) the SQL needed to drop the current database schema
  doctrine:schema:update                     Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata
  doctrine:schema:validate                   Validate the mapping files
 fos
  fos:js-routing:debug                       Displays currently exposed routes for an application
  fos:js-routing:dump                        Dumps exposed routes to the filesystem
 lint
  lint:container                             Ensures that arguments injected into services match type declarations
  lint:twig                                  Lints a template and outputs encountered errors
  lint:xliff                                 Lints a XLIFF file and outputs encountered errors
  lint:yaml                                  Lints a file and outputs encountered errors
 make
  make:auth                                  Creates a Guard authenticator of different flavors
  make:command                               Creates a new console command class
  make:controller                            Creates a new controller class
  make:crud                                  Creates CRUD for Doctrine entity class
  make:docker:database                       Adds a database container to your docker-compose.yaml file
  make:entity                                Creates or updates a Doctrine entity class, and optionally an API Platform resource
  make:fixtures                              Creates a new class to load Doctrine fixtures
  make:form                                  Creates a new form class
  make:message                               Creates a new message and handler
  make:messenger-middleware                  Creates a new messenger middleware
  make:migration                             Creates a new migration based on database changes
  make:registration-form                     Creates a new registration form system
  make:reset-password                        Create controller, entity, and repositories for use with symfonycasts/reset-password-bundle
  make:serializer:encoder                    Creates a new serializer encoder class
  make:serializer:normalizer                 Creates a new serializer normalizer class
  make:subscriber                            Creates a new event subscriber class
  make:table-meta                            Creates classes with table data
  make:test                                  [make:unit-test|make:functional-test] Creates a new test class
  make:twig-extension                        Creates a new Twig extension class
  make:user                                  Creates a new security user class
  make:validator                             Creates a new validator and constraint class
  make:voter                                 Creates a new security voter class
 reset-password
  reset-password:remove-expired              Remove expired reset password requests from persistence.
 router
  router:match                               Helps debug routes by simulating a path info match
 secrets
  secrets:decrypt-to-local                   Decrypts all secrets and stores them in the local vault
  secrets:encrypt-from-local                 Encrypts all local secrets to the vault
  secrets:generate-keys                      Generates new encryption keys
  secrets:list                               Lists all secrets
  secrets:remove                             Removes a secret from the vault
  secrets:set                                Sets a secret in the vault security
  security:encode-password                   Encodes a password
  server
  server:dump                                Starts a dump server that collects and displays dumps in a single place
  server:log                                 Starts a log server that displays logs in real time
 translation
  translation:update                         Updates the translation file

Wow that is a lot of commands. Enter the list command in your terminal to get a better view.

mr rodgers how it is done meme
And that is how it is done!!!
Categories
Random Software Development

How to go back multiple directories in a linux terminal command line

Often when I am using the command line in a terminal on Linux I need to go back more than one directory. Usually you use the command

cd ..

Which takes you back one level in a directory. For example if you are in /var/www/http and you type cd .. you will be in directory /var/www/.

But what if you are super deep in the directory and need to get back to www directory. Say you are in /var/www/http/website/public and you want to get back to /var/www/http you can either type:
cd .. ( inside /var/www/http/website/ )
cd .. (inside /var/www/http/ now )

But you can type the following and get all the way back to the /www/http/ directory in one line
cd ../..
That line will take you back two directories. If you need to go further back just continue adding ../..

Categories
Software Development Web Development

How to fix Github git keeps asking for password with ssh keys

This was a super annoying issue I have had for years. I never looked into it because I was lazy.

I setup my ssh keys like you probably did and I kept getting prompted for my password.

I kept entering it for years because the github docs are not that great and fail to mention how to fully setup the keys properly(maybe changed by now). I kept wondering why it was asking me to enter my password even with the ssh keys.

Well a few days ago August 13th 2021, github switched from allowing password ssh git push/pull to not allowing it and forcing you to use something like ssh keys. I went to push to my github repo and was told I had to use ssh keys blah blah…

I was like

spock WTF
Wait. WTF?

I thought I had setup the ssh-keys. But, what had happened was I had used the HTTPS method to pull the repo. So my git config was set to the HTTPS endpoint not the SSH endpoint. It had been a long time ago. I thought I had setup the keys, and I did. What I failed to do was switch the git remote value in the git config.

are you kidding me
Freaking seriously

Basically if you pulled a repo via https then you need to switch your git configuration to use the ssh url. I won’t make this article longer writing how to do that, here is a great, short article that explains it

You also need to know the ssh repo value which you can find in the repository under clone like this

github clone
remote repo name

I have not tried the newer GitHub CLI. I will eventually read about it and try it and update my repos to use it. I did write about using github ssh deploy keys here though.  And about using multiple deploy ssh keys here.

Categories
Software Development Web Development

How to get URL Routes in your Javascript in Symfony 5+

WARNING :  The current docs that were in the symfony docs are 404 right now. The only documentation is found in the library itself. I’ll review it and rewrite this article once I get it working.

so shocked

This is something I needed to know how to do so I could generate URLs inside my javascript more easily.

Currently the docs on this are outdated so I will record what I did here so I can know later, or for others who need this info.

The docs say something about app/appKernel.php that file no longer exists.  It has been replaced with just kernel.php and you do pretty much nothing to that file.

I am not sure what version Symfony started this but I know in 5+ you don’t have to register the bundle like step 2 of the old docs shows. This is done automatically for you in a new file named config/bundles.php which is where all bundles are automatically registered when you install them.

So all you do is this command now in the terminal

composer require friendsofsymfony/jsrouting-bundle

inside your apps root level. It is auto registered for you.

You don’t have to do step 3 of the old docs either as the config system has changed. There is no

app/config/routing.yml

file anymore. When you install the bundle the new system creates a new file named app/config/routes/fos_js_routing.yaml for you
For step 4 the required code has changed you need to enter this now.

$ php bin/console assets:install --symlink public

Basically to install the bundle you just use the composer command and most of the stuff is now done for you.

A silent secret.

In order for the routes to work you must add something new to every single route definition you want to be able to generate a route in your javascript for.

You must add this to the annotation or where ever you define your routes. I use annotations so I can just look to the controllers, plus when I split the system into microservices the routes go with their services.

options={"expose"=true}

So for my menu route the definition in the controller looks like this.

@Route("/menu", name="menu", options={"expose"=true}, methods={"GET"})
</code.

Now how to use it?

So now that it is installed how do you use it? One more step. Now you must include the needed Javascript in your page with these tags.

<script src="{{ asset('bundles/fosjsrouting/js/router.js') }}"></script>
<script src="{{ path('fos_js_routing_js', { callback: 'fos.Router.setData' }) }}"></script>

Place those anywhere you want. What I did is put them in my base twig template in a section where I include my Javascript this way it is available on every page because I will probably be needing access to routes as I build the app.

Once you have all of the above done you can use it like this

let testUrl = Routing.generate( 'menu');
console.log(testUrl); //outputs /menu

Route not found errors?

Yes I got these too. You must add expose

options={"expose"=true}

to every single route you want to use. And your IDE probably won’t be very helpful so double check the spellings. Routing probably won’t be found by your IDE.

Here is a link to my favorite HTMLEntities converter if you write articles about programming you will need this.

 

Categories
Software Development Web Development

How to create and use a custom Javascript Event

You have probably used events in Javascript many times.  Especially if you have done any User Interface programming. There are many types of events provided by browsers and the Javascript engines.

Did you know you can create your own custom events with the CustomEvent() constructor? Here is a minimal example. In this article I will explain custom events and how to use them.

First off what would you use a custom event for? You use them to notify other objects in your app that actions have occurred such as “user clicked x” or “user closed dialog”. Using events prevents code coupling and reduces dependencies.

I came across this need when creating a dialog box where I wanted an overlay to show beneath it blocking out the page behind. I didn’t want my DialogBox to have to know about my Overlay.  I didn’t want them coupled. I didn’t want my DialogBox to have to have an Overlay object as an argument creating a dependency. I didn’t want my DialogBox to even know that an Overlay object existed.

The answer is for my DialogBox to emit/create/dispatch a custom Javascript Event and have a listener for that event to close the Overlay.

So lets look at some code.


import {Utils} from "./Utils";

class DialogBox {

    /**
     *
     * @param {string} dialogId
     * @param {string} dialogClass
     */
    constructor(dialogId = 'dialogId', dialogClass = '') {
        this.closeDialogId = 'closeDialog';
        this.dialogBoxBottomRowId = 'dialogBoxBottomRow';
        this.dialogBoxClass = 'dialogBox ' + dialogClass;
        this.contentContainerId = 'dialogContentContainer';
        this.dialogHtml = '';
        this.dialogBoxId = dialogId;
        this.closeHandler = null;
        this.divElement = null;
    }

    /**
     *
     * @param {string} rowContent : the content HTML etc to be added as a row
     * @param {string} cssClass : applied only if passed in
     * @returns {void}
     */
    bottomRow(rowContent = '', cssClass = 'dialog-bottom-row') {

        let bottomRow = document.getElementById(this.dialogBoxBottomRowId);

        //if the bottom row does not exist add it to the html
        if ( Utils.isEmpty(bottomRow)) {
            let rowHtml = '<div id="' + this.dialogBoxBottomRowId + '" ';
            rowHtml += ' class="' + cssClass + '" ';
            rowHtml += ' >' + rowContent + '</div>';
            this.dialogHtml += rowHtml;
        } else {
            //if a bottom row exists replace it
            bottomRow.className = cssClass;
            bottomRow.innerText = rowContent;
        }
    }
    /**
     * Centers the dialog vertically and horizontally in the parent element
     * @param {string} parentElementId
     */
    centerDialog(parentElementId = 'body') {

        let parentWidth = 0;
        let parentHeight = 0;
        //need the dialog boxes calculated width and height
        let dialogHeight = this.divElement.clientHeight;
        let dialogWidth = this.divElement.clientWidth;

        //must use two different ways to get the height and width
        if (parentElementId === 'body') {
            parentWidth = window.innerWidth;
            parentHeight = window.innerHeight;
        } else {
            let parentElement = document.getElementById(parentElementId);
            //make sure null or undefined were not returned
            if (!Utils.isEmpty(parentElement)) {
                parentHeight = parentElement.clientHeight;
                parentWidth = parentElement.clientWidth;
            }
        }
        let left = (parentWidth / 2) - (dialogWidth / 2);
        let top = (parentHeight / 2) - (dialogHeight / 2);
        //must add px or it doesn't work at all
        this.divElement.style.top = top + 'px';
        this.divElement.style.left = left + 'px';
    }
    /**
     *
     * @param {string} content
     * @param {string} containerClass
     */
    contentContainer(content, containerClass = 'dialog-content') {
        let contentDiv = document.getElementById(this.contentContainerId);

        //if the container exists replace it contents
        if (Utils.isEmpty(contentDiv)) {
            let contentHtml = '<div id="' + this.contentContainerId + '"';
            contentHtml += ' class="' + containerClass + '" >';
            contentHtml += content + '</div>';
            this.dialogHtml += contentHtml;
        } else {
            contentDiv.innerHTML = content;
            contentDiv.className = containerClass;
        }
    }
    /**
     * calls removeDialogBox which removes the dialog and event listeners
     */
    hideDialogBox() {
        this.removeDialogBox();
    }

    /**
     *
     * @param {string} menuText
     * @param {string} menuTextClass
     */
    menuBar(menuText, menuTextClass = '') {
        let menuTextId = 'dialog-menu-text';
        /*
         * if dialogMenuBar is present then the length will be non zero or true
         * if this is the case replace the content, this allows this method to be called
         * again later to change the value
         */
        let menuBarID = 'dialog-menu-bar';
        let menuTextDiv = document.getElementById(menuTextId);

        if (Utils.isEmpty(menuTextDiv)) {
            this.dialogHtml = '<div class="dialog-menu-bar" id="dialog-menu-bar" >';
            this.dialogHtml += '<div id="' + menuTextId + '" class="dialog-menu-text ';
            this.dialogHtml += menuTextClass + '" >' + menuText + '</div>';
            this.dialogHtml += '<div id="' + this.closeDialogId + '" class="close-dialog" >';
            this.dialogHtml += '<img src="/images/drawing/close-window.png" ';
            this.dialogHtml += 'alt="Close dialog" >';
            this.dialogHtml += '</div></div>';
        } else {
            menuTextDiv.innerText = menuText;
            menuTextDiv.className = menuTextClass;
        }
    }

    /**
     * displays the dialog box, you must call centerDialog to center it
     */
    showDialogBox() {
        //remove any existing dialog boxes first
        this.removeDialogBox();
        this.divElement = document.createElement("div");
        this.divElement.id = this.dialogBoxId;
        this.divElement.className = this.dialogBoxClass;
        this.divElement.innerHTML = this.dialogHtml;
        //position the dialog box now give it the highest z-index to be on top
        this.divElement.style.zIndex = Utils.getHighestZIndex() + 1;
        document.body.appendChild(this.divElement);
        //add the listener for when the user clicks to close

        this.closeHandler = function ( ) {
        this.removeDialogBox();
        }.bind(this);

        let close = document.getElementById(this.closeDialogId);
        close.addEventListener('click', this.closeHandler, false);
    }

    /**
     * removes the dialog html from the page and removes the close listener
     * dispatches event 'dialogClosed' to be used to close an overlay etc.
     */
    removeDialogBox() {
        let dialogElem = document.getElementById(this.dialogBoxId);
        //if a dialog box of the same id exists delete it first to prevent errors and issues
        if (dialogElem) {
            let close = document.getElementById(this.closeDialogId);
            close.removeEventListener('click', this.closeHandler, false);
            dialogElem.remove();
            const dialogEvent = new CustomEvent('dialogClosed');
            document.body.dispatchEvent(dialogEvent);
        }
    }
}

export {DialogBox}

That is a lot of code 169 lines to be exact. I am still in the process of converting this code, still going to add some Template literals instead of the old fashioned string concatenation technique.

There are several very import things to note here in this code. For example the way the addEventListener() and removeEventListener are used. These functions have to be passed THE EXACT SAME parameters or removeEventListener() fails to remove the event listener and that clutters your memory up because you will have listeners referring to elements that don’t exist.

That is why I have this code this.closeHandler


//add the listener for when the user clicks to close

        this.closeHandler = function ( ) {
        this.removeDialogBox();
        }.bind(this);

        let close = document.getElementById(this.closeDialogId);
        close.addEventListener('click', this.closeHandler, false);

See this.closeHandler = function  that stores the function to handle the click on the close button. Both the add and remove event listener functions have to be passed the exact same function.

Look at the removeDialogBox function closer.


let dialogElem = document.getElementById(this.dialogBoxId);
        //if a dialog box of the same id exists delete it first to prevent errors and issues
        if (dialogElem) {
            let close = document.getElementById(this.closeDialogId);
            close.removeEventListener('click', this.closeHandler, false);
            dialogElem.remove();
            const dialogEvent = new CustomEvent('dialogClosed');
            document.body.dispatchEvent(dialogEvent);
        }

Notice that the second argument to removeEventListener is this.closeHandler that is the same function passed to the addEventListener above.

If you use anonymous functions inside add and remove event listeners instead, then they wont be the same function and so your event listener won’t be removed and your memory fills up faster.

Another important note is that the this.handler function must use bind(this) like so

this.closeHandler = function ( ) { this.removeDialogBox(); }.bind(this);

If you don’t bind the function expression then you will get an error about this.removeDialogBox is not a function.
This is because you are storing the closeHandler in memory for later use.  At that later time the context will be different, the code won’t be executing within your class anymore, it will be in it’s own context. That means “this” that was alive in your class, no longer exists. Which means that function no longer exists You must bind “this” by using “.bind(this) at the end of the function.

And now about the Custom Event. You will see it at the bottom of the removeDialogbox() function

const dialogEvent = new CustomEvent('dialogClosed');
document.body.dispatchEvent(dialogEvent);

Those two lines is all it takes to create and dispatch your own Custom event. This means you write code that listens for the custom “dialogClosed” event to be fired like this.

document.body.addEventListener('dialogClosed', function (){
overlay.hideOverlay();
});

Notice I am using document.body this is a very easy way to create the listener. This is using an anonymous function which is bad because this listener can’t be removed. It should be removed right below this. In order to do that you would need to create the handler function above it and pass it to both the add and remove event listeners.

Adding information to the event.
This is one of the most handy parts of custom events, the ability to pass information in the event. This can be any information, a full object even.
In this article it mentions adding custom data with “detail”.  Here is an example from the code above.

const dialogEvent = new CustomEvent('dialogClosed',{ detail: {
        id: this.dialogBoxId
    } });

Here I am passing id you can use this same format to pass many more values just add a comma to the end of each one. Then to access the extra information in your listener you do like this.

document.body.addEventListener( 'dialogClosed', function (event) {

let dialogId = event.detail.id
switch (dialogId) {
case mainDialogId :
mainOverlay.hideOverlay();
break;
case colorDialogId :
overlay2.hideOverlay();
break;
}

}, false);

Above I am using event.detail.id to get the value I stored in id in the detail of the custom event. Notice how I am using a switch statement to compare the id’s of the dialog that closed to close the correct overlay. There is no default behavior for this action, either one dialog closes or another. I could have 5 different dialogs if I wanted.

Categories
Software Development Web Development Web Security

Authentication vs Authorization what is the difference?

Authentication/Authorization these terms are often confused. Here I will clarify them.

Authentication — Login, proving who a user is one way or another. After a user is logged into a system a session cookie is usually created to re-authenticate the user so they don’t have to login every single page view.

Authorization — Can a user view or access something once Authenticated? Authorization includes things like administration panel access, viewing a users profile or post or media etc.

Categories
Resources Software Development

Ethereum Solidity programming links and resources.

Links and resources about Ethereum Solidity programming language.

Videos

Categories
Software Development Web Development

How to remove unused or broken docker container images.

Sometimes we make mistakes. When first learning docker we probably make many mistakes and end up with tons of unused docker images.

If you are on Linux like me, you won’t have a desktop dashboard like Mac and Windows get, so things are harder. To see a list of what images you have created you use the following command

docker ps -a

That command will output something like this.
It will show the CONTAINER ID, IMAGE, COMMAND etc. as you can see. To delete an image you use the container id with docker rm like this.

docker rm b5f8fae52bce

I’ve seen older internet posts using the IMAGE value but I had no success with that method. I am guessing something changed. I didn’t even see an example of this I just tried it. I don’t see any mention of this in the docs either. But it works. This part in the getting started intro actually explains it. I think something did change.

Docker container rm documentation.

Docker rm documentation.

Categories
Resources Software Development Web Development

HTTP headers and caching resources.

Resources all about HTTP headers and caching.

Caching tutorial -> great article to start with, explains all the basics of caching.

Hypertext Transfer Protocol (HTTP/1.1): Caching  rfc spec