Cannot get to work tax_query array for terms

The problem is that you’re passing a string with commas in it, not an actual array.

The result of:

foreach ( $categories as $cat ) {
    $filter = get_term_by( 'id', $cat, 'portfolio-category' );

    if ( ! empty( $filter ) ) {
        $query_filter .= $filter->slug . ',';
    }
}

Is:

'one,two,three,'

Not an array. So you’re querying for portfolio items that are in the “one,two,three,” category, which presumably doesn’t exist.

If you want to query multiple slugs, you need to pass an array, and to do that you need to build an array, which would look like this:

$query_filter = []; // Initialise empty array.

foreach ( $categories as $cat ) {
    $filter = get_term_by( 'id', $cat, 'portfolio-category' );

    if ( ! empty( $filter ) ) {
        $query_filter[] = $filter->slug; // Add slug to array.
    }
}

However. None of this is even necessary. I can see from your code that $categories is already an array of IDs. This means you can just pass $categories to the query directly. There’s no need to bother getting the slugs:

$args = array(
    'post_type' => 'portfolio',
    'tax_query' => array(
         rray(
                'taxonomy' => 'portfolio-category',
                'field'    => 'term_id',
                'terms'    => $categories,
        ),
    ),
    'paged'          => $paged,
    'posts_per_page' => $projects_per_page,
);