I'm writing this post because I've spent a week trying to figure this one out - and there isn't a one-size-fits all solution :) At least from the manhours perspective.

So, after implementing simple lihgting in my toy game engine, I wanted to add more than 1 light. But since the light source coordinates are passed to the shader as uniform values, the number of lights was limited (to 1).

There are basically 2 (inteligent) ways to tackle this problem.

1.The hard way: deferred shading.

In short, there is a great tutorial for this: https://learnopengl.com/Advanced-Lighting/Deferred-Shading. The idea is to postpone the lighting process (push it to "post-processing"). While this is the right way to handle multiple lights, it is not the easiest way to do so (modern OpenGL 3 features usage, messy debugging, and overall lots o' code!)

2.The easy way: limited number of light sources

In your shader, make the light source coordinates an uniform array of a reasonable size (I've seen people store 4 light sources). This way you limit the number of lights that affect a certain object, but it is usually OK.

But how to pick those 4 light sources if you have, lets say, 10 lights in your scene?

Make a quick pass through the scene and sort the lights using some kind of a metric. A great metric, for starters, is the distance from the light source to the object that is being rendered. This way you only take into account the light sources that are closest to your object.

A more intelligent metric would be to multiply the distance with the intensity of the light -- thus creating some sort of a "weighted sum" effect.

But you quickly run into problems if you have batched rendering, with batching focus on materials. Some instances of a material can be far apart from the rest of them -- therefore making our "distance" metric pretty bad. We need to take all instances of a material into consideration before picking our 4 light sources. The easies way of doing this (at least the easiest one that I've found) is to iterate through all 4-element subsets of your light sources set, and calculate the total dispersion from the all of the material instances. Then you pick the set which had the minimal dispersion, and transfer it as a aforementioned uniform.

Its a bit messy to implement, and it doesn't scale very well with number of lights in the scene, but for scenes with <20 light sources it usually works fine. It will surely be "enough" to allow you to focus on other parts of the rendering system until you manage to find the time to implement deferred shading. At least that is the strategy I'm using.