As I progress with time, I notice that it's quite difficult to navigate in my files and search where particular functionality is coded.
This is the classic problem with procedural programming. Code is written where it's needed at the time the need was discovered. This leads to spaghetti code, which is an apt metaphor but alternatively can be understood as a code knot where each addition to your codebase adds a new layer obscuring to the layers beneath, making it harder to unravel and comprehend. At a certain size understanding how a code knot works requires heroic memorization and patience.
For single file programs (like shell scripts) this isn't always bad. Programs composed of less than 50 lines can be read through and understood in a single sitting. Most web applications (especially those backed by a database) do not scale well in a procedural format.
How to know when the knot is going to hang you
Could you recommend me some guidelines, how to structure my application, to be more easier to navigate in the code and append some new functionalities?
That is entirely dependent on the requirements of your application. If you wrote your project in a procedural style then you probably structured your code in ad-hoc manner. As you saw a piece that could be reused you probably turned that into a new file and started "including/requiring" that file.
As you scale this style will always end in tragedy because you've created a rigid temporal coupling between different concerns. The order of execution tends to be specific and therefore very brittle. Each new include adds fragility that ripples across your codebase. Need to customize one of the includes? You want to skip an include, except it contains key logic that another element requires? You need to add a new state but can't figure out where to initialize it? Naming clashes become more common as you scale and all kinds of unwieldy variables start spreading across your codebase, meaning different things at different times. Suddenly a change in
footer.php inexplicably breaks
sidebar.php and you have no idea why.
It's at this point you redesign your codebase to isolate concerns, instead of spread them.
One simple way to do this is to encapsulate each kind of knowledge in a class, and using static properties of that class to namespace your different global states. This is the first step towards using objects even though it's just proceduralism in disguise.
How to untie the knot
I know that the resulting design will probably be the MVC pattern. What I'm interested in is in the evolution from the chaotic design in the old times to more and more structural design typical in these days. (It's for educational purposes, to realize the evolution)
This topic really deserves a pamphlet, or small book — it'd be a great work. I've poked around dozens of "MVC" frameworks in a variety of languages. Very few frameworks start procedural and move to OO — it's rarely a smooth, easy to articulate evolution.
Reading OO articles is usually not illuminating, until after you've started writing OO code. Even within an OO codebase there will be lingering elements of procedural cruft, making it hard to see why objects even exist. It's only when you see a bunch of well designed objects working together, in a low-friction, easy to reason about way can you fully grok the elegance of OO.
Designing useful, transparent, illuminating objects is hard, and there is plenty of overly designed OO code that violates the 4th rule of simple design: minimize. Bad OO code will frustrate, mislead, and distract you from the primary goal of high level software: to express intent in a way that both humans and machines can understand reasonably well.
Process to OO
Could you show this progress on some minimal examples or direct me to some design/architecture guidelines, where is captured this historical progress through PHP web application development?
As you struggle with useful conventions in a procedural projects you may never need Object Orientated design to be successful. But as scale your spaghetti you will inevitable run into a genre of frustrations that are much easier to deal with when you know how to design and coordinate, coherent objects.
Moving from procedural to object oriented is a journey, not a destination.
Start by reading: Create your own framework on top of the Symfony components
Then compare how different frameworks use those same components to excellent effect:
MVC in general
MVC can be a red herring since there is no fixed meaning for what a controller, model, or view object look like (from an interface/public api perspective). I'd characterize a handful of characteristics.
A good application framework should:
- follow "Inversion of Control" often implemented using a Dependency Injection (summarized as it's better to depend on a flexible abstract definition over a specific concrete definition)
- Have dumb Views and smart Models (aka: views should be logic-light, and presentation heavy)
- Offer you a convenient way (via a Controller or some other pattern, like Events + Observers/Presenters) to write simple glue statements that allow you to connect your Models and Views without model objects, or view objects having knowledge (i.e. holding instances) of each other.
- Code should be organized into highly cohesive, low coupling modules and packages. Especially reusable code.
As a paragraph:
Views are dumb and work roughly the same across many frameworks (with dozens of similar templating languages). Models are smart they can be organized and conceptualized in a variety of ways but should be the "engine" objects describing (and driving) the purpose of your application. Controllerish objects usually connect these dots, although they may trigger these connections through some delegation (like Events). In web frameworks controllers tend to be associated with accessing a URL, mouse clicks or any number of user initiated events.