@@ -1821,6 +1821,167 @@ public function testDefaultsCanBeCombinedWithExtraQueryParameters()
1821
1821
$ url ->route ('tenantPostUser ' , ['concretePost ' , 'extra ' => 'query ' ]),
1822
1822
);
1823
1823
}
1824
+
1825
+ public function testUrlGenerationWithOptionalParameters (): void
1826
+ {
1827
+ $ url = new UrlGenerator (
1828
+ $ routes = new RouteCollection ,
1829
+ Request::create ('https://www.foo.com/ ' )
1830
+ );
1831
+
1832
+ $ url ->defaults ([
1833
+ 'tenant ' => 'defaultTenant ' ,
1834
+ 'user ' => 'defaultUser ' ,
1835
+ ]);
1836
+
1837
+ /**
1838
+ * Route with one required parameter and one optional parameter.
1839
+ */
1840
+ $ route = new Route (['GET ' ], 'postOptionalMethod/{post}/{method?} ' , ['as ' => 'postOptionalMethod ' , fn () => '' ]);
1841
+ $ routes ->add ($ route );
1842
+
1843
+ $ this ->assertSame (
1844
+ 'https://www.foo.com/postOptionalMethod/1 ' ,
1845
+ $ url ->route ('postOptionalMethod ' , 1 ),
1846
+ );
1847
+
1848
+ $ this ->assertSame (
1849
+ 'https://www.foo.com/postOptionalMethod/1/2 ' ,
1850
+ $ url ->route ('postOptionalMethod ' , [1 , 2 ]),
1851
+ );
1852
+
1853
+ /**
1854
+ * Route with two optional parameters.
1855
+ */
1856
+ $ route = new Route (['GET ' ], 'optionalPostOptionalMethod/{post}/{method?} ' , ['as ' => 'optionalPostOptionalMethod ' , fn () => '' ]);
1857
+ $ routes ->add ($ route );
1858
+
1859
+ $ this ->assertSame (
1860
+ 'https://www.foo.com/optionalPostOptionalMethod/1 ' ,
1861
+ $ url ->route ('optionalPostOptionalMethod ' , 1 ),
1862
+ );
1863
+
1864
+ $ this ->assertSame (
1865
+ 'https://www.foo.com/optionalPostOptionalMethod/1/2 ' ,
1866
+ $ url ->route ('optionalPostOptionalMethod ' , [1 , 2 ]),
1867
+ );
1868
+
1869
+ /**
1870
+ * Route with one default parameter, one required parameter, and one optional parameter.
1871
+ */
1872
+ $ route = new Route (['GET ' ], 'tenantPostOptionalMethod/{tenant}/{post}/{method?} ' , ['as ' => 'tenantPostOptionalMethod ' , fn () => '' ]);
1873
+ $ routes ->add ($ route );
1874
+
1875
+ // Passing one parameter
1876
+ $ this ->assertSame (
1877
+ 'https://www.foo.com/tenantPostOptionalMethod/defaultTenant/concretePost ' ,
1878
+ $ url ->route ('tenantPostOptionalMethod ' , ['concretePost ' ]),
1879
+ );
1880
+
1881
+ // Passing two parameters: optional parameter is prioritized over parameter with a default value
1882
+ $ this ->assertSame (
1883
+ 'https://www.foo.com/tenantPostOptionalMethod/defaultTenant/concretePost/concreteMethod ' ,
1884
+ $ url ->route ('tenantPostOptionalMethod ' , ['concretePost ' , 'concreteMethod ' ]),
1885
+ );
1886
+
1887
+ // Passing all three parameters
1888
+ $ this ->assertSame (
1889
+ 'https://www.foo.com/tenantPostOptionalMethod/concreteTenant/concretePost/concreteMethod ' ,
1890
+ $ url ->route ('tenantPostOptionalMethod ' , ['concreteTenant ' , 'concretePost ' , 'concreteMethod ' ]),
1891
+ );
1892
+
1893
+ /**
1894
+ * Route with two default parameters, one required parameter, and one optional parameter.
1895
+ */
1896
+ $ route = new Route (['GET ' ], 'tenantUserPostOptionalMethod/{tenant}/{user}/{post}/{method?} ' , ['as ' => 'tenantUserPostOptionalMethod ' , fn () => '' ]);
1897
+ $ routes ->add ($ route );
1898
+
1899
+ // Passing one parameter
1900
+ $ this ->assertSame (
1901
+ 'https://www.foo.com/tenantUserPostOptionalMethod/defaultTenant/defaultUser/concretePost ' ,
1902
+ $ url ->route ('tenantUserPostOptionalMethod ' , ['concretePost ' ]),
1903
+ );
1904
+
1905
+ // Passing two parameters: optional parameter is prioritized over parameters with default values
1906
+ $ this ->assertSame (
1907
+ 'https://www.foo.com/tenantUserPostOptionalMethod/defaultTenant/defaultUser/concretePost/concreteMethod ' ,
1908
+ $ url ->route ('tenantUserPostOptionalMethod ' , ['concretePost ' , 'concreteMethod ' ]),
1909
+ );
1910
+
1911
+ // Passing three parameters: only the leftmost parameter with a default value uses its default value
1912
+ $ this ->assertSame (
1913
+ 'https://www.foo.com/tenantUserPostOptionalMethod/defaultTenant/concreteUser/concretePost/concreteMethod ' ,
1914
+ $ url ->route ('tenantUserPostOptionalMethod ' , ['concreteUser ' , 'concretePost ' , 'concreteMethod ' ]),
1915
+ );
1916
+
1917
+ // Same as the assertion above, but using some named parameters
1918
+ $ this ->assertSame (
1919
+ 'https://www.foo.com/tenantUserPostOptionalMethod/defaultTenant/concreteUser/concretePost/concreteMethod ' ,
1920
+ $ url ->route ('tenantUserPostOptionalMethod ' , ['user ' => 'concreteUser ' , 'concretePost ' , 'concreteMethod ' ]),
1921
+ );
1922
+
1923
+ // Also using a named parameter, but this time for the post parameter
1924
+ $ this ->assertSame (
1925
+ 'https://www.foo.com/tenantUserPostOptionalMethod/defaultTenant/concreteUser/concretePost/concreteMethod ' ,
1926
+ $ url ->route ('tenantUserPostOptionalMethod ' , ['concreteUser ' , 'post ' => 'concretePost ' , 'concreteMethod ' ]),
1927
+ );
1928
+
1929
+ // Also using a named parameter, but this time for the optional method parameter
1930
+ $ this ->assertSame (
1931
+ 'https://www.foo.com/tenantUserPostOptionalMethod/defaultTenant/concreteUser/concretePost/concreteMethod ' ,
1932
+ $ url ->route ('tenantUserPostOptionalMethod ' , ['concreteUser ' , 'concretePost ' , 'method ' => 'concreteMethod ' ]),
1933
+ );
1934
+
1935
+ // Passing all four parameters
1936
+ $ this ->assertSame (
1937
+ 'https://www.foo.com/tenantUserPostOptionalMethod/concreteTenant/concreteUser/concretePost/concreteMethod ' ,
1938
+ $ url ->route ('tenantUserPostOptionalMethod ' , ['concreteTenant ' , 'concreteUser ' , 'concretePost ' , 'concreteMethod ' ]),
1939
+ );
1940
+
1941
+ /**
1942
+ * Route with a default parameter, a required parameter, another default parameter, and finally an optional parameter.
1943
+ *
1944
+ * This tests interleaved default parameters when combined with optional parameters.
1945
+ */
1946
+ $ route = new Route (['GET ' ], 'tenantPostUserOptionalMethod/{tenant}/{post}/{user}/{method?} ' , ['as ' => 'tenantPostUserOptionalMethod ' , fn () => '' ]);
1947
+ $ routes ->add ($ route );
1948
+
1949
+ // Passing one parameter
1950
+ $ this ->assertSame (
1951
+ 'https://www.foo.com/tenantPostUserOptionalMethod/defaultTenant/concretePost/defaultUser ' ,
1952
+ $ url ->route ('tenantPostUserOptionalMethod ' , ['concretePost ' ]),
1953
+ );
1954
+
1955
+ // Passing two parameters: optional parameter is prioritized over parameters with default values
1956
+ $ this ->assertSame (
1957
+ 'https://www.foo.com/tenantPostUserOptionalMethod/defaultTenant/concretePost/defaultUser/concreteMethod ' ,
1958
+ $ url ->route ('tenantPostUserOptionalMethod ' , ['concretePost ' , 'concreteMethod ' ]),
1959
+ );
1960
+
1961
+ // Same as the assertion above, but using some named parameters
1962
+ $ this ->assertSame (
1963
+ 'https://www.foo.com/tenantPostUserOptionalMethod/defaultTenant/concretePost/defaultUser/concreteMethod ' ,
1964
+ $ url ->route ('tenantPostUserOptionalMethod ' , ['post ' => 'concretePost ' , 'concreteMethod ' ]),
1965
+ );
1966
+
1967
+ // Also using a named parameter, but this time for the optional parameter
1968
+ $ this ->assertSame (
1969
+ 'https://www.foo.com/tenantPostUserOptionalMethod/defaultTenant/concretePost/defaultUser/concreteMethod ' ,
1970
+ $ url ->route ('tenantPostUserOptionalMethod ' , ['concretePost ' , 'method ' => 'concreteMethod ' ]),
1971
+ );
1972
+
1973
+ // Passing three parameters: only the leftmost parameter with a default value uses its default value
1974
+ $ this ->assertSame (
1975
+ 'https://www.foo.com/tenantPostUserOptionalMethod/defaultTenant/concretePost/concreteUser/concreteMethod ' ,
1976
+ $ url ->route ('tenantPostUserOptionalMethod ' , ['concretePost ' , 'concreteUser ' , 'concreteMethod ' ]),
1977
+ );
1978
+
1979
+ // Passing all four parameters
1980
+ $ this ->assertSame (
1981
+ 'https://www.foo.com/tenantPostUserOptionalMethod/concreteTenant/concretePost/concreteUser/concreteMethod ' ,
1982
+ $ url ->route ('tenantPostUserOptionalMethod ' , ['concreteTenant ' , 'concretePost ' , 'concreteUser ' , 'concreteMethod ' ]),
1983
+ );
1984
+ }
1824
1985
}
1825
1986
1826
1987
class RoutableInterfaceStub implements UrlRoutable
0 commit comments